summaryrefslogtreecommitdiff
path: root/tb
diff options
context:
space:
mode:
Diffstat (limited to 'tb')
-rw-r--r--tb/avalon.hpp58
-rw-r--r--tb/avalon.impl.hpp17
-rw-r--r--tb/interrupt.cpp63
-rw-r--r--tb/top/conspiracion.cpp9
-rw-r--r--tb/vga_domain.sv1
5 files changed, 138 insertions, 10 deletions
diff --git a/tb/avalon.hpp b/tb/avalon.hpp
index 347c8bd..95bbe78 100644
--- a/tb/avalon.hpp
+++ b/tb/avalon.hpp
@@ -11,7 +11,7 @@ namespace taller::avalon
class slave
{
public:
- inline slave(std::uint32_t base, std::uint32_t size, std::size_t word_size)
+ inline slave(std::uint32_t base, std::uint32_t size, std::size_t word_size) noexcept
: base(base),
mask(~(size - 1)),
word(log2i(word_size))
@@ -59,6 +59,11 @@ namespace taller::avalon
virtual bool read(std::uint32_t addr, std::uint32_t &data) = 0;
virtual bool write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable) = 0;
+ inline virtual bool irq() noexcept
+ {
+ return false;
+ }
+
private:
std::uint32_t base;
std::uint32_t mask;
@@ -70,6 +75,39 @@ namespace taller::avalon
}
};
+ struct irq_lines
+ {
+ slave *timer = nullptr;
+ slave *jtaguart = nullptr;
+ };
+
+ class interrupt_controller : private slave
+ {
+ public:
+ interrupt_controller(std::uint32_t base) noexcept;
+
+ virtual bool read(std::uint32_t addr, std::uint32_t &data) noexcept final override;
+ virtual bool write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable) noexcept final override;
+
+ virtual bool irq() noexcept;
+
+ inline slave &as_slave() noexcept
+ {
+ return *this;
+ }
+
+ inline irq_lines &lines() noexcept
+ {
+ return irqs;
+ }
+
+ private:
+ irq_lines irqs;
+ std::uint32_t mask = 0;
+
+ std::uint32_t status() noexcept;
+ };
+
template<class Platform>
class interconnect
{
@@ -78,6 +116,7 @@ namespace taller::avalon
bool tick(bool clk);
void attach(slave &dev);
+ void attach_intc(interrupt_controller &intc);
void bail() noexcept;
bool dump(std::uint32_t addr, std::uint32_t &word);
@@ -91,14 +130,15 @@ namespace taller::avalon
slave &dev;
};
- Platform &plat;
- slave* active = nullptr;
- std::vector<binding> devices;
- std::uint32_t avl_address = 0;
- std::uint32_t avl_writedata = 0;
- unsigned avl_byteenable = 0;
- bool avl_read = false;
- bool avl_write = false;
+ Platform &plat;
+ slave* active = nullptr;
+ std::vector<binding> devices;
+ interrupt_controller *root_intc = nullptr;
+ std::uint32_t avl_address = 0;
+ std::uint32_t avl_writedata = 0;
+ unsigned avl_byteenable = 0;
+ bool avl_read = false;
+ bool avl_write = false;
slave *resolve_external(std::uint32_t avl_address);
};
diff --git a/tb/avalon.impl.hpp b/tb/avalon.impl.hpp
index 6f4bfb9..e04c7b0 100644
--- a/tb/avalon.impl.hpp
+++ b/tb/avalon.impl.hpp
@@ -22,16 +22,28 @@ namespace taller::avalon
}
template<class Platform>
+ void interconnect<Platform>::attach_intc(interrupt_controller &intc)
+ {
+ assert(root_intc == nullptr);
+
+ attach(intc.as_slave());
+ root_intc = &intc;
+ }
+
+ template<class Platform>
bool interconnect<Platform>::tick(bool clk)
{
if(!plat.reset_reset_n) [[unlikely]]
{
active = nullptr;
+ plat.avl_irq = 0;
+
avl_read = false;
avl_write = false;
avl_address = 0;
avl_writedata = 0;
avl_byteenable = 0;
+
return true;
}
@@ -59,6 +71,11 @@ namespace taller::avalon
binding.dev.tick();
}
+ if(root_intc)
+ {
+ plat.avl_irq = root_intc->irq();
+ }
+
if(!active)
{
avl_address = plat.avl_address;
diff --git a/tb/interrupt.cpp b/tb/interrupt.cpp
new file mode 100644
index 0000000..4164bb7
--- /dev/null
+++ b/tb/interrupt.cpp
@@ -0,0 +1,63 @@
+#include <cstdint>
+
+#include "avalon.hpp"
+
+namespace taller::avalon
+{
+ interrupt_controller::interrupt_controller(std::uint32_t base) noexcept
+ : slave(base, 8, 4)
+ {}
+
+ bool interrupt_controller::read(std::uint32_t addr, std::uint32_t &data) noexcept
+ {
+ switch(addr)
+ {
+ case 0:
+ data = status();
+ break;
+
+ case 1:
+ data = mask;
+ break;
+ }
+
+ return true;
+ }
+
+ bool interrupt_controller::write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable) noexcept
+ {
+ switch(addr)
+ {
+ case 0:
+ break;
+
+ case 1:
+ mask = data;
+ break;
+ }
+
+ return true;
+ }
+
+ bool interrupt_controller::irq() noexcept
+ {
+ return status() != 0;
+ }
+
+ std::uint32_t interrupt_controller::status() noexcept
+ {
+ std::uint32_t lines = 0;
+
+ if(irqs.timer)
+ {
+ lines |= irqs.timer->irq() << 0;
+ }
+
+ if(irqs.jtaguart)
+ {
+ lines |= irqs.jtaguart->irq() << 1;
+ }
+
+ return lines & mask;
+ }
+}
diff --git a/tb/top/conspiracion.cpp b/tb/top/conspiracion.cpp
index de43952..67ad2db 100644
--- a/tb/top/conspiracion.cpp
+++ b/tb/top/conspiracion.cpp
@@ -300,6 +300,12 @@ int main(int argc, char **argv)
mem<std::uint32_t> hps_ddr3(0x0000'0000, 512 << 20);
jtag_uart ttyJ0(0x3000'0000);
interval_timer timer(0x3002'0000);
+ interrupt_controller intc(0x3007'0000);
+
+ auto &irq_lines = intc.lines();
+ irq_lines.jtaguart = &ttyJ0;
+ irq_lines.timer = &timer;
+
mem<std::uint32_t> vram(0x3800'0000, 64 << 20);
null vram_null(0x3800'0000, 64 << 20, 2);
window vram_window(vram, 0x0000'0000);
@@ -323,8 +329,9 @@ int main(int argc, char **argv)
bool enable_accurate_video = !headless && accurate_video;
avl.attach(hps_ddr3);
- avl.attach(ttyJ0);
avl.attach(timer);
+ avl.attach(ttyJ0);
+ avl.attach_intc(intc);
for(auto &slave : consts)
{
diff --git a/tb/vga_domain.sv b/tb/vga_domain.sv
index d3ffbc0..0c9aac5 100644
--- a/tb/vga_domain.sv
+++ b/tb/vga_domain.sv
@@ -7,6 +7,7 @@ module vga_domain
logic[25:0] avl_address /*verilator public*/;
logic avl_read /*verilator public*/;
logic avl_write /*verilator public*/;
+ logic avl_irq /*verilator public_flat_rw @(negedge clk_clk)*/;
logic[15:0] avl_readdata /*verilator public_flat_rw @(negedge clk_clk)*/;
logic[31:0] avl_writedata /*verilator public*/;
logic avl_waitrequest /*verilator public_flat_rw @(negedge clk_clk)*/;