diff options
| -rw-r--r-- | rtl/intc.sv | 30 | ||||
| -rw-r--r-- | tb/avalon.hpp | 58 | ||||
| -rw-r--r-- | tb/avalon.impl.hpp | 17 | ||||
| -rw-r--r-- | tb/interrupt.cpp | 63 | ||||
| -rw-r--r-- | tb/top/conspiracion.cpp | 9 | ||||
| -rw-r--r-- | tb/vga_domain.sv | 1 |
6 files changed, 168 insertions, 10 deletions
diff --git a/rtl/intc.sv b/rtl/intc.sv new file mode 100644 index 0000000..af78ef8 --- /dev/null +++ b/rtl/intc.sv @@ -0,0 +1,30 @@ +module intc +( + input logic clk, + rst_n, + + input logic irq_timer, + irq_jtaguart, + + input logic avl_address, + avl_read, + avl_write, + input logic[31:0] avl_writedata, + + output logic avl_irq, + output logic[31:0] avl_readdata +); + + logic[31:0] status, mask; + + assign status = {30'b0, irq_jtaguart, irq_timer} & mask; + assign avl_irq = |status; + assign avl_readdata = avl_address ? mask : status; + + always @(posedge clk or negedge rst_n) + if(!rst_n) + mask <= 0; + else if(avl_write && avl_address) + mask <= avl_writedata; + +endmodule 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)*/; |
