Add AXI4-Lite CPUIF

This commit is contained in:
Alex Mykyta
2022-01-31 23:11:31 -08:00
parent 321d8a6cd1
commit de5eecf0e7
8 changed files with 483 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 %}

View File

@@ -19,6 +19,12 @@ class ModelSim(Simulator):
# Ignore noisy warning about vopt-time checking of always_comb/always_latch # Ignore noisy warning about vopt-time checking of always_comb/always_latch
"-suppress", "2583", "-suppress", "2583",
# all warnings are errors
"-warning", "error",
# except this one.. TODO: figure out if I can avoid this
"-suppress", "13314",
] ]
# Add source files # Add source files

View File

@@ -1,12 +1,15 @@
from itertools import product from itertools import product
from .cpuifs.apb3 import APB3, FlatAPB3 from .cpuifs.apb3 import APB3, FlatAPB3
from .cpuifs.axi4lite import AXI4Lite, FlatAXI4Lite
from .cpuifs.passthrough import Passthrough from .cpuifs.passthrough import Passthrough
all_cpuif = [ all_cpuif = [
APB3(), APB3(),
FlatAPB3(), FlatAPB3(),
AXI4Lite(),
FlatAXI4Lite(),
Passthrough(), Passthrough(),
] ]