diff --git a/peakrdl/regblock/cpuif/axi4lite/__init__.py b/peakrdl/regblock/cpuif/axi4lite/__init__.py new file mode 100644 index 0000000..71af396 --- /dev/null +++ b/peakrdl/regblock/cpuif/axi4lite/__init__.py @@ -0,0 +1,49 @@ +from ..base import CpuifBase + +class AXI4Lite_Cpuif(CpuifBase): + template_path = "cpuif/axi4lite/axi4lite_tmpl.sv" + + @property + def port_declaration(self) -> str: + return "axi4lite_intf.slave s_axil" + + def signal(self, name:str) -> str: + return "s_axil." + name.upper() + + @property + def data_width_bytes(self) -> int: + return self.data_width // 8 + + +class AXI4Lite_Cpuif_flattened(AXI4Lite_Cpuif): + @property + def port_declaration(self) -> str: + lines = [ + "output logic " + self.signal("awready"), + "input wire " + self.signal("awvalid"), + f"input wire [{self.addr_width-1}:0] " + self.signal("awaddr"), + "input wire [2:0] " + self.signal("awprot"), + + "output logic " + self.signal("wready"), + "input wire " + self.signal("wvalid"), + f"input wire [{self.data_width-1}:0] " + self.signal("wdata"), + f"input wire [{self.data_width//8-1}:0]" + self.signal("wstrb"), + + "input wire " + self.signal("bready"), + "output logic " + self.signal("bvalid"), + "output logic [1:0] " + self.signal("bresp"), + + "output logic " + self.signal("arready"), + "input wire " + self.signal("arvalid"), + f"input wire [{self.addr_width-1}:0] " + self.signal("araddr"), + "input wire [2:0] " + self.signal("arprot"), + + "input wire " + self.signal("rready"), + "output logic " + self.signal("rvalid"), + f"output logic [{self.data_width-1}:0] " + self.signal("rdata"), + "output logic [1:0] " + self.signal("rresp"), + ] + return ",\n".join(lines) + + def signal(self, name:str) -> str: + return "s_axil_" + name diff --git a/peakrdl/regblock/cpuif/axi4lite/axi4lite_tmpl.sv b/peakrdl/regblock/cpuif/axi4lite/axi4lite_tmpl.sv new file mode 100644 index 0000000..756e15b --- /dev/null +++ b/peakrdl/regblock/cpuif/axi4lite/axi4lite_tmpl.sv @@ -0,0 +1,102 @@ +enum logic [1:0] { + CPUIF_IDLE, + CPUIF_BRESP, + CPUIF_RRESP +} cpuif_state; + +logic cpuif_prev_was_rd; +always_ff {{get_always_ff_event(cpuif.reset)}} begin + if({{get_resetsignal(cpuif.reset)}}) begin + cpuif_state <= CPUIF_IDLE; + cpuif_prev_was_rd <= '0; + + cpuif_req <= '0; + cpuif_req_is_wr <= '0; + cpuif_addr <= '0; + cpuif_wr_data <= '0; + + {{cpuif.signal("arready")}} <= '0; + {{cpuif.signal("awready")}} <= '0; + {{cpuif.signal("wready")}} <= '0; + {{cpuif.signal("bvalid")}} <= '0; + {{cpuif.signal("bresp")}} <= '0; + {{cpuif.signal("rvalid")}} <= '0; + {{cpuif.signal("rdata")}} <= '0; + {{cpuif.signal("rresp")}} <= '0; + end else begin + // Load response transfers as they arrive + if(cpuif_rd_ack) begin + {{cpuif.signal("rvalid")}} <= '1; + {{cpuif.signal("rdata")}} <= cpuif_rd_data; + if(cpuif_rd_err) {{cpuif.signal("rresp")}} <= 2'b10; // SLVERR + else {{cpuif.signal("rresp")}} <= 2'b00; // OKAY + end + if(cpuif_wr_ack) begin + {{cpuif.signal("bvalid")}} <= '1; + if(cpuif_wr_err) {{cpuif.signal("bresp")}} <= 2'b10; // SLVERR + else {{cpuif.signal("bresp")}} <= 2'b00; // OKAY + end + + // Transaction state machine + case(cpuif_state) + CPUIF_IDLE: begin + // round-robin arbitrate between read/write requests + // Allow read if previous transfer was not a read, or no write is active + if({{cpuif.signal("arvalid")}} && (!cpuif_prev_was_rd || !{{cpuif.signal("awvalid")}} || !{{cpuif.signal("wvalid")}})) begin + cpuif_req <= '1; + cpuif_req_is_wr <= '0; + {%- if cpuif.data_width == 8 %} + cpuif_addr <= {{cpuif.signal("araddr")}}[{{cpuif.addr_width-1}}:0]; + {%- else %} + cpuif_addr <= { {{-cpuif.signal("araddr")}}[{{cpuif.addr_width-1}}:{{clog2(cpuif.data_width_bytes)}}], {{clog2(cpuif.data_width_bytes)}}'b0}; + {%- endif %} + {{cpuif.signal("arready")}} <= '1; + cpuif_state <= CPUIF_RRESP; + end else if({{cpuif.signal("awvalid")}} && {{cpuif.signal("wvalid")}}) begin + {{cpuif.signal("awready")}} <= '1; + {{cpuif.signal("wready")}} <= '1; + if({{cpuif.signal("wstrb")}} != {{"%d'b" % cpuif.data_width_bytes}}{{"1" * cpuif.data_width_bytes}}) begin + // Unaligned writes or use of byte strobes is not supported yet + {{cpuif.signal("bvalid")}} <= '1; + {{cpuif.signal("bresp")}} <= 2'b10; // SLVERR + end else begin + cpuif_req <= '1; + cpuif_req_is_wr <= '1; + {%- if cpuif.data_width == 8 %} + cpuif_addr <= {{cpuif.signal("awaddr")}}[{{cpuif.addr_width-1}}:0]; + {%- else %} + cpuif_addr <= { {{-cpuif.signal("awaddr")}}[{{cpuif.addr_width-1}}:{{clog2(cpuif.data_width_bytes)}}], {{clog2(cpuif.data_width_bytes)}}'b0}; + {%- endif %} + cpuif_wr_data <= {{cpuif.signal("wdata")}}; + end + cpuif_state <= CPUIF_BRESP; + end + end + + CPUIF_BRESP: begin + cpuif_req <= '0; + {{cpuif.signal("awready")}} <= '0; + {{cpuif.signal("wready")}} <= '0; + cpuif_prev_was_rd <= '0; + if({{cpuif.signal("bvalid")}} && {{cpuif.signal("bready")}}) begin + {{cpuif.signal("bvalid")}} <= '0; + cpuif_state <= CPUIF_IDLE; + end + end + + CPUIF_RRESP: begin + cpuif_req <= '0; + {{cpuif.signal("arready")}} <= '0; + cpuif_prev_was_rd <= '1; + if({{cpuif.signal("rvalid")}} && {{cpuif.signal("rready")}}) begin + {{cpuif.signal("rvalid")}} <= '0; + cpuif_state <= CPUIF_IDLE; + end + end + + default: begin + cpuif_state <= CPUIF_IDLE; + end + endcase + end +end diff --git a/test/lib/cpuifs/axi4lite/__init__.py b/test/lib/cpuifs/axi4lite/__init__.py new file mode 100644 index 0000000..6fc236c --- /dev/null +++ b/test/lib/cpuifs/axi4lite/__init__.py @@ -0,0 +1,14 @@ +from ..base import CpuifTestMode + +from peakrdl.regblock.cpuif.axi4lite import AXI4Lite_Cpuif, AXI4Lite_Cpuif_flattened + +class AXI4Lite(CpuifTestMode): + cpuif_cls = AXI4Lite_Cpuif + tb_files = [ + "axi4lite_intf.sv", + "axi4lite_intf_driver.sv", + ] + tb_template = "tb_inst.sv" + +class FlatAXI4Lite(AXI4Lite): + cpuif_cls = AXI4Lite_Cpuif_flattened diff --git a/test/lib/cpuifs/axi4lite/axi4lite_intf.sv b/test/lib/cpuifs/axi4lite/axi4lite_intf.sv new file mode 100644 index 0000000..b0a232d --- /dev/null +++ b/test/lib/cpuifs/axi4lite/axi4lite_intf.sv @@ -0,0 +1,80 @@ +interface axi4lite_intf #( + parameter DATA_WIDTH = 32, + parameter ADDR_WIDTH = 32 +); + logic AWREADY; + logic AWVALID; + logic [ADDR_WIDTH-1:0] AWADDR; + logic [2:0] AWPROT; + + logic WREADY; + logic WVALID; + logic [DATA_WIDTH-1:0] WDATA; + logic [DATA_WIDTH/8-1:0] WSTRB; + + logic BREADY; + logic BVALID; + logic [1:0] BRESP; + + logic ARREADY; + logic ARVALID; + logic [ADDR_WIDTH-1:0] ARADDR; + logic [2:0] ARPROT; + + logic RREADY; + logic RVALID; + logic [DATA_WIDTH-1:0] RDATA; + logic [1:0] RRESP; + + modport master ( + input AWREADY, + output AWVALID, + output AWADDR, + output AWPROT, + + input WREADY, + output WVALID, + output WDATA, + output WSTRB, + + output BREADY, + input BVALID, + input BRESP, + + input ARREADY, + output ARVALID, + output ARADDR, + output ARPROT, + + output RREADY, + input RVALID, + input RDATA, + input RRESP + ); + + modport slave ( + output AWREADY, + input AWVALID, + input AWADDR, + input AWPROT, + + output WREADY, + input WVALID, + input WDATA, + input WSTRB, + + input BREADY, + output BVALID, + output BRESP, + + output ARREADY, + input ARVALID, + input ARADDR, + input ARPROT, + + input RREADY, + output RVALID, + output RDATA, + output RRESP + ); +endinterface diff --git a/test/lib/cpuifs/axi4lite/axi4lite_intf_driver.sv b/test/lib/cpuifs/axi4lite/axi4lite_intf_driver.sv new file mode 100644 index 0000000..522b515 --- /dev/null +++ b/test/lib/cpuifs/axi4lite/axi4lite_intf_driver.sv @@ -0,0 +1,175 @@ +interface axi4lite_intf_driver #( + parameter DATA_WIDTH = 32, + parameter ADDR_WIDTH = 32 + )( + input wire clk, + input wire rst, + axi4lite_intf.master m_axil + ); + + timeunit 1ps; + timeprecision 1ps; + + logic AWREADY; + logic AWVALID; + logic [ADDR_WIDTH-1:0] AWADDR; + logic [2:0] AWPROT; + + logic WREADY; + logic WVALID; + logic [DATA_WIDTH-1:0] WDATA; + logic [DATA_WIDTH/8-1:0] WSTRB; + + logic BREADY; + logic BVALID; + logic [1:0] BRESP; + + logic ARREADY; + logic ARVALID; + logic [ADDR_WIDTH-1:0] ARADDR; + logic [2:0] ARPROT; + + logic RREADY; + logic RVALID; + logic [DATA_WIDTH-1:0] RDATA; + logic [1:0] RRESP; + + assign AWREADY = m_axil.AWREADY; + assign m_axil.AWVALID = AWVALID; + assign m_axil.AWADDR = AWADDR; + assign m_axil.AWPROT = AWPROT; + assign WREADY = m_axil.WREADY; + assign m_axil.WVALID = WVALID; + assign m_axil.WDATA = WDATA; + assign m_axil.WSTRB = WSTRB; + assign m_axil.BREADY = BREADY; + assign BVALID = m_axil.BVALID; + assign BRESP = m_axil.BRESP; + assign ARREADY = m_axil.ARREADY; + assign m_axil.ARVALID = ARVALID; + assign m_axil.ARADDR = ARADDR; + assign m_axil.ARPROT = ARPROT; + assign m_axil.RREADY = RREADY; + assign RVALID = m_axil.RVALID; + assign RDATA = m_axil.RDATA; + assign RRESP = m_axil.RRESP; + + default clocking cb @(posedge clk); + default input #1step output #1; + input AWREADY; + output AWVALID; + output AWADDR; + output AWPROT; + input WREADY; + output WVALID; + output WDATA; + output WSTRB; + inout BREADY; + input BVALID; + input BRESP; + input ARREADY; + output ARVALID; + output ARADDR; + output ARPROT; + inout RREADY; + input RVALID; + input RDATA; + input RRESP; + endclocking + + task reset(); + cb.AWVALID <= '0; + cb.AWADDR <= '0; + cb.AWPROT <= '0; + cb.WVALID <= '0; + cb.WDATA <= '0; + cb.WSTRB <= '0; + cb.ARVALID <= '0; + cb.ARADDR <= '0; + cb.ARPROT <= '0; + endtask + + initial forever begin + cb.RREADY <= $urandom_range(1, 0); + cb.BREADY <= $urandom_range(1, 0); + @cb; + end + + task write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data); + bit w_before_aw; + w_before_aw = $urandom_range(1,0); + ##0; + + fork + begin + if(w_before_aw) repeat($urandom_range(2,0)) @cb; + cb.AWVALID <= '1; + cb.AWADDR <= addr; + cb.AWPROT <= '0; + @(cb); + while(cb.AWREADY !== 1'b1) @(cb); + cb.AWVALID <= '0; + end + + begin + if(!w_before_aw) repeat($urandom_range(2,0)) @cb; + cb.WVALID <= '1; + cb.WDATA <= data; + cb.WSTRB <= '1; // TODO: Support byte strobes + @(cb); + while(cb.WREADY !== 1'b1) @(cb); + cb.WVALID <= '0; + cb.WSTRB <= '0; + end + + begin + while(cb.BREADY !== 1'b1 && cb.BVALID !== 1'b1) @(cb); + assert(!$isunknown(cb.BRESP)) else $error("Read from 0x%0x returned X's on BRESP", addr); + end + join + endtask + + task read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data); + ##0; + + fork + begin + cb.ARVALID <= '1; + cb.ARADDR <= addr; + cb.ARPROT <= '0; + @(cb); + while(cb.ARREADY !== 1'b1) @(cb); + cb.ARVALID <= '0; + end + + begin + @cb; + while(!(cb.RREADY === 1'b1 && cb.RVALID === 1'b1)) @(cb); + assert(!$isunknown(cb.RDATA)) else $error("Read from 0x%0x returned X's on RDATA", addr); + assert(!$isunknown(cb.RRESP)) else $error("Read from 0x%0x returned X's on RRESP", addr); + data = cb.RDATA; + end + join + endtask + + task assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1); + logic [DATA_WIDTH-1:0] data; + read(addr, data); + data &= mask; + assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data); + endtask + + initial begin + reset(); + end + + initial forever begin + @cb; + if(!rst) assert(!$isunknown(cb.AWREADY)) else $error("Saw X on AWREADY!"); + if(!rst) assert(!$isunknown(cb.WREADY)) else $error("Saw X on WREADY!"); + if(!rst) assert(!$isunknown(cb.BVALID)) else $error("Saw X on BVALID!"); + if(!rst) assert(!$isunknown(cb.ARREADY)) else $error("Saw X on ARREADY!"); + if(!rst) assert(!$isunknown(cb.RVALID)) else $error("Saw X on RVALID!"); + end + +endinterface diff --git a/test/lib/cpuifs/axi4lite/tb_inst.sv b/test/lib/cpuifs/axi4lite/tb_inst.sv new file mode 100644 index 0000000..5b3f6fa --- /dev/null +++ b/test/lib/cpuifs/axi4lite/tb_inst.sv @@ -0,0 +1,54 @@ +{% sv_line_anchor %} +axi4lite_intf #( + .DATA_WIDTH({{exporter.cpuif.data_width}}), + .ADDR_WIDTH({{exporter.cpuif.addr_width}}) +) s_axil(); +axi4lite_intf_driver #( + .DATA_WIDTH({{exporter.cpuif.data_width}}), + .ADDR_WIDTH({{exporter.cpuif.addr_width}}) +) cpuif ( + .clk(clk), + .rst(rst), + .m_axil(s_axil) +); +{% if type(cpuif).__name__.startswith("Flat") %} +{% sv_line_anchor %} +wire s_axil_awready; +wire s_axil_awvalid; +wire [{{exporter.cpuif.addr_width - 1}}:0] s_axil_awaddr; +wire [2:0] s_axil_awprot; +wire s_axil_wready; +wire s_axil_wvalid; +wire [{{exporter.cpuif.data_width - 1}}:0] s_axil_wdata; +wire [{{exporter.cpuif.data_width_bytes - 1}}:0] s_axil_wstrb; +wire s_axil_bready; +wire s_axil_bvalid; +wire [1:0] s_axil_bresp; +wire s_axil_arready; +wire s_axil_arvalid; +wire [{{exporter.cpuif.addr_width - 1}}:0] s_axil_araddr; +wire [2:0] s_axil_arprot; +wire s_axil_rready; +wire s_axil_rvalid; +wire [{{exporter.cpuif.data_width - 1}}:0] s_axil_rdata; +wire [1:0] s_axil_rresp; +assign s_axil.AWREADY = s_axil_awready; +assign s_axil_awvalid = s_axil.AWVALID; +assign s_axil_awaddr = s_axil.AWADDR; +assign s_axil_awprot = s_axil.AWPROT; +assign s_axil.WREADY = s_axil_wready; +assign s_axil_wvalid = s_axil.WVALID; +assign s_axil_wdata = s_axil.WDATA; +assign s_axil_wstrb = s_axil.WSTRB; +assign s_axil_bready = s_axil.BREADY; +assign s_axil.BVALID = s_axil_bvalid; +assign s_axil.BRESP = s_axil_bresp; +assign s_axil.ARREADY = s_axil_arready; +assign s_axil_arvalid = s_axil.ARVALID; +assign s_axil_araddr = s_axil.ARADDR; +assign s_axil_arprot = s_axil.ARPROT; +assign s_axil_rready = s_axil.RREADY; +assign s_axil.RVALID = s_axil_rvalid; +assign s_axil.RDATA = s_axil_rdata; +assign s_axil.RRESP = s_axil_rresp; +{% endif %} diff --git a/test/lib/simulators/modelsim.py b/test/lib/simulators/modelsim.py index 10e4128..0a6f4d2 100644 --- a/test/lib/simulators/modelsim.py +++ b/test/lib/simulators/modelsim.py @@ -19,6 +19,12 @@ class ModelSim(Simulator): # Ignore noisy warning about vopt-time checking of always_comb/always_latch "-suppress", "2583", + + # all warnings are errors + "-warning", "error", + + # except this one.. TODO: figure out if I can avoid this + "-suppress", "13314", ] # Add source files diff --git a/test/lib/test_params.py b/test/lib/test_params.py index 6f18685..14ffd68 100644 --- a/test/lib/test_params.py +++ b/test/lib/test_params.py @@ -1,12 +1,15 @@ from itertools import product from .cpuifs.apb3 import APB3, FlatAPB3 +from .cpuifs.axi4lite import AXI4Lite, FlatAXI4Lite from .cpuifs.passthrough import Passthrough all_cpuif = [ APB3(), FlatAPB3(), + AXI4Lite(), + FlatAXI4Lite(), Passthrough(), ]