added apb3 and axi4_lite
This commit is contained in:
@@ -54,14 +54,14 @@ interface axi4lite_intf #(
|
||||
|
||||
modport slave (
|
||||
output AWREADY,
|
||||
input AWVALID,
|
||||
input AWADDR,
|
||||
// input AWVALID,
|
||||
// input AWADDR,
|
||||
input AWPROT,
|
||||
|
||||
output WREADY,
|
||||
input WVALID,
|
||||
input WDATA,
|
||||
input WSTRB,
|
||||
// input WVALID,
|
||||
// input WDATA,
|
||||
// input WSTRB,
|
||||
|
||||
input BREADY,
|
||||
output BVALID,
|
||||
@@ -73,8 +73,8 @@ interface axi4lite_intf #(
|
||||
input ARPROT,
|
||||
|
||||
input RREADY,
|
||||
output RVALID,
|
||||
output RDATA,
|
||||
output RRESP
|
||||
// output RVALID,
|
||||
// output RDATA,
|
||||
// output RRESP
|
||||
);
|
||||
endinterface
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import overload
|
||||
|
||||
from systemrdl.node import AddressableNode
|
||||
|
||||
from ...utils import get_indexed_path
|
||||
@@ -10,11 +12,11 @@ class APB3Cpuif(BaseCpuif):
|
||||
|
||||
def _port_declaration(self, child: AddressableNode) -> str:
|
||||
base = f"apb3_intf.master m_apb_{child.inst_name}"
|
||||
if not child.is_array:
|
||||
if child.array_dimensions is None:
|
||||
return base
|
||||
if child.current_idx is not None:
|
||||
return f"{base}_{'_'.join(map(str, child.current_idx))}"
|
||||
return f"{base} [N_{child.inst_name.upper()}S]"
|
||||
return f"{base}_{'_'.join(map(str, child.current_idx))} {''.join(f'[{dim}]' for dim in child.array_dimensions)}"
|
||||
return f"{base} {''.join(f'[{dim}]' for dim in child.array_dimensions)}"
|
||||
|
||||
@property
|
||||
def port_declaration(self) -> str:
|
||||
@@ -23,36 +25,48 @@ class APB3Cpuif(BaseCpuif):
|
||||
|
||||
return ",\n".join(slave_ports + master_ports)
|
||||
|
||||
def signal(
|
||||
self,
|
||||
signal: str,
|
||||
node: AddressableNode | None = None,
|
||||
) -> str:
|
||||
if node is None:
|
||||
@overload
|
||||
def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
|
||||
@overload
|
||||
def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ...
|
||||
def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
|
||||
if node is None or indexer is None:
|
||||
# Node is none, so this is a slave signal
|
||||
return f"s_apb.{signal}"
|
||||
|
||||
# Master signal
|
||||
return f"m_apb_{node.inst_name}.{signal}"
|
||||
return f"m_apb_{get_indexed_path(node.parent, node, indexer, skip_kw_filter=True)}.{signal}"
|
||||
|
||||
def fanout(self, node: AddressableNode, idx: str | None = None) -> str:
|
||||
def fanout(self, node: AddressableNode) -> str:
|
||||
fanout: dict[str, str] = {}
|
||||
fanout[self.signal("PSEL", node, idx)] = (
|
||||
f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node)}|cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node)}"
|
||||
fanout[self.signal("PSEL", node, "gi")] = (
|
||||
f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}|cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
|
||||
)
|
||||
fanout[self.signal("PSEL", node, idx)] = self.signal("PSEL")
|
||||
fanout[self.signal("PWRITE", node, idx)] = (
|
||||
f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node)}"
|
||||
fanout[self.signal("PENABLE", node, "gi")] = self.signal("PENABLE")
|
||||
fanout[self.signal("PWRITE", node, "gi")] = (
|
||||
f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
|
||||
)
|
||||
fanout[self.signal("PADDR", node, idx)] = self.signal("PADDR")
|
||||
fanout[self.signal("PWDATA", node, idx)] = "cpuif_wr_data"
|
||||
fanout[self.signal("PADDR", node, "gi")] = self.signal("PADDR")
|
||||
fanout[self.signal("PWDATA", node, "gi")] = "cpuif_wr_data"
|
||||
|
||||
return "\n".join(map(lambda kv: f"assign {kv[0]} = {kv[1]};", fanout.items()))
|
||||
|
||||
def fanin(self, node: AddressableNode, idx: str | None = None) -> str:
|
||||
def fanin(self, node: AddressableNode | None = None) -> str:
|
||||
fanin: dict[str, str] = {}
|
||||
fanin["cpuif_rd_data"] = self.signal("PRDATA", node, idx)
|
||||
fanin["cpuif_rd_ack"] = self.signal("PREADY", node, idx)
|
||||
fanin["cpuif_rd_err"] = self.signal("PSLVERR", node, idx)
|
||||
if node is None:
|
||||
fanin["cpuif_rd_ack"] = "'0"
|
||||
fanin["cpuif_rd_err"] = "'0"
|
||||
else:
|
||||
fanin["cpuif_rd_ack"] = self.signal("PREADY", node, "i")
|
||||
fanin["cpuif_rd_err"] = self.signal("PSLVERR", node, "i")
|
||||
|
||||
return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
|
||||
|
||||
def readback(self, node: AddressableNode | None = None) -> str:
|
||||
fanin: dict[str, str] = {}
|
||||
if node is None:
|
||||
fanin["cpuif_rd_data"] = "'0"
|
||||
else:
|
||||
fanin["cpuif_rd_data"] = self.signal("PRDATA", node, "i")
|
||||
|
||||
return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
|
||||
|
||||
@@ -9,15 +9,18 @@
|
||||
`endif
|
||||
{% endif -%}
|
||||
|
||||
assign cpuif_req = {{cpuif.signal("PSELx")}};
|
||||
assign cpuif_req = {{cpuif.signal("PSEL")}};
|
||||
assign cpuif_wr_en = {{cpuif.signal("PWRITE")}};
|
||||
assign cpuif_rd_en = !{{cpuif.signal("PWRITE")}};
|
||||
|
||||
assign cpuif_wr_addr = {{cpuif.signal("PADDR")}};
|
||||
assign cpuif_rd_addr = {{cpuif.signal("PADDR")}};
|
||||
|
||||
assign cpuif_wr_data = {{cpuif.signal("PWDATA")}};
|
||||
|
||||
assign {{cpuif.signal("PRDATA")}} = cpuif_rd_data;
|
||||
assign {{cpuif.signal("PREADY")}} = cpuif_rd_ack;
|
||||
assign {{cpuif.signal("PSLVERR")}} = cpuif_rd_err;
|
||||
assign {{cpuif.signal("PSLVERR")}} = cpuif_rd_err | cpuif_rd_sel.cpuif_err | cpuif_wr_sel.cpuif_err;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Fanout CPU Bus interface signals
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
from ..base_cpuif import CpuifBase
|
||||
|
||||
|
||||
class AXI4Lite_Cpuif(CpuifBase):
|
||||
template_path = "axi4lite_tmpl.sv"
|
||||
is_interface = True
|
||||
|
||||
@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 busdecoder_latency(self) -> int:
|
||||
return max(self.exp.ds.min_read_latency, self.exp.ds.min_write_latency)
|
||||
|
||||
@property
|
||||
def max_outstanding(self) -> int:
|
||||
"""
|
||||
Best pipelined performance is when the max outstanding transactions
|
||||
is the design's latency + 2.
|
||||
Anything beyond that does not have any effect, aside from adding unnecessary
|
||||
logic and additional buffer-bloat latency.
|
||||
"""
|
||||
return self.busdecoder_latency + 2
|
||||
|
||||
@property
|
||||
def resp_buffer_size(self) -> int:
|
||||
"""
|
||||
Response buffer size must be greater or equal to max outstanding
|
||||
transactions to prevent response overrun.
|
||||
"""
|
||||
return self.max_outstanding
|
||||
|
||||
|
||||
class AXI4Lite_Cpuif_flattened(AXI4Lite_Cpuif):
|
||||
is_interface = False
|
||||
|
||||
@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_bytes - 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
|
||||
89
src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif.py
Normal file
89
src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif.py
Normal file
@@ -0,0 +1,89 @@
|
||||
from typing import overload
|
||||
|
||||
from systemrdl.node import AddressableNode
|
||||
|
||||
from ...utils import get_indexed_path
|
||||
from ..base_cpuif import BaseCpuif
|
||||
|
||||
|
||||
class AXI4LiteCpuif(BaseCpuif):
|
||||
template_path = "axi4lite_tmpl.sv"
|
||||
is_interface = True
|
||||
|
||||
def _port_declaration(self, child: AddressableNode) -> str:
|
||||
base = f"axi4lite_intf.master m_axil_{child.inst_name}"
|
||||
if child.array_dimensions is None:
|
||||
return base
|
||||
if child.current_idx is not None:
|
||||
return f"{base}_{'_'.join(map(str, child.current_idx))} {''.join(f'[{dim}]' for dim in child.array_dimensions)}"
|
||||
return f"{base} {''.join(f'[{dim}]' for dim in child.array_dimensions)}"
|
||||
|
||||
@property
|
||||
def port_declaration(self) -> str:
|
||||
"""Returns the port declaration for the AXI4-Lite interface."""
|
||||
slave_ports: list[str] = ["axi4lite_intf.slave s_axil"]
|
||||
master_ports: list[str] = list(map(self._port_declaration, self.addressable_children))
|
||||
|
||||
return ",\n".join(slave_ports + master_ports)
|
||||
|
||||
@overload
|
||||
def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
|
||||
@overload
|
||||
def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ...
|
||||
def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
|
||||
if node is None or indexer is None:
|
||||
# Node is none, so this is a slave signal
|
||||
return f"s_axil.{signal}"
|
||||
|
||||
# Master signal
|
||||
return f"m_axil_{get_indexed_path(node.parent, node, indexer, skip_kw_filter=True)}.{signal}"
|
||||
|
||||
def fanout(self, node: AddressableNode) -> str:
|
||||
fanout: dict[str, str] = {}
|
||||
|
||||
wr_sel = f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
|
||||
rd_sel = f"cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
|
||||
|
||||
# Write address channel
|
||||
fanout[self.signal("AWVALID", node, "gi")] = wr_sel
|
||||
fanout[self.signal("AWADDR", node, "gi")] = self.signal("AWADDR")
|
||||
fanout[self.signal("AWPROT", node, "gi")] = self.signal("AWPROT")
|
||||
|
||||
# Write data channel
|
||||
fanout[self.signal("WVALID", node, "gi")] = wr_sel
|
||||
fanout[self.signal("WDATA", node, "gi")] = "cpuif_wr_data"
|
||||
fanout[self.signal("WSTRB", node, "gi")] = "cpuif_wr_byte_en"
|
||||
|
||||
# Write response channel (master -> slave)
|
||||
fanout[self.signal("BREADY", node, "gi")] = self.signal("BREADY")
|
||||
|
||||
# Read address channel
|
||||
fanout[self.signal("ARVALID", node, "gi")] = rd_sel
|
||||
fanout[self.signal("ARADDR", node, "gi")] = self.signal("ARADDR")
|
||||
fanout[self.signal("ARPROT", node, "gi")] = self.signal("ARPROT")
|
||||
|
||||
# Read data channel (master -> slave)
|
||||
fanout[self.signal("RREADY", node, "gi")] = self.signal("RREADY")
|
||||
|
||||
return "\n".join(f"assign {lhs} = {rhs};" for lhs, rhs in fanout.items())
|
||||
|
||||
def fanin(self, node: AddressableNode | None = None) -> str:
|
||||
fanin: dict[str, str] = {}
|
||||
if node is None:
|
||||
fanin["cpuif_rd_ack"] = "'0"
|
||||
fanin["cpuif_rd_err"] = "'0"
|
||||
else:
|
||||
# Read side: ack comes from RVALID; err if RRESP[1] is set (SLVERR/DECERR)
|
||||
fanin["cpuif_rd_ack"] = self.signal("RVALID", node, "i")
|
||||
fanin["cpuif_rd_err"] = f"{self.signal('RRESP', node, 'i')}[1]"
|
||||
|
||||
return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items())
|
||||
|
||||
def readback(self, node: AddressableNode | None = None) -> str:
|
||||
fanin: dict[str, str] = {}
|
||||
if node is None:
|
||||
fanin["cpuif_rd_data"] = "'0"
|
||||
else:
|
||||
fanin["cpuif_rd_data"] = self.signal("RDATA", node, "i")
|
||||
|
||||
return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items())
|
||||
@@ -1,254 +1,60 @@
|
||||
{%- if cpuif.is_interface -%}
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
assert_bad_addr_width: assert($bits({{cpuif.signal("araddr")}}) >= {{ds.package_name}}::{{ds.module_name.upper()}}_MIN_ADDR_WIDTH)
|
||||
else $error("Interface address width of %0d is too small. Shall be at least %0d bits", $bits({{cpuif.signal("araddr")}}), {{ds.package_name}}::{{ds.module_name.upper()}}_MIN_ADDR_WIDTH);
|
||||
assert_bad_data_width: assert($bits({{cpuif.signal("wdata")}}) == {{ds.package_name}}::{{ds.module_name.upper()}}_DATA_WIDTH)
|
||||
else $error("Interface data width of %0d is incorrect. Shall be %0d bits", $bits({{cpuif.signal("wdata")}}), {{ds.package_name}}::{{ds.module_name.upper()}}_DATA_WIDTH);
|
||||
end
|
||||
`endif
|
||||
// Width checks (AXI4-Lite)
|
||||
assert_bad_awaddr_width: assert($bits({{cpuif.signal("AWADDR")}}) >= {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH)
|
||||
else $error("AWADDR width %0d < MIN_ADDR_WIDTH %0d",
|
||||
$bits({{cpuif.signal("AWADDR")}}), {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH);
|
||||
|
||||
assert_bad_araddr_width: assert($bits({{cpuif.signal("ARADDR")}}) >= {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH)
|
||||
else $error("ARADDR width %0d < MIN_ADDR_WIDTH %0d",
|
||||
$bits({{cpuif.signal("ARADDR")}}), {{ds.package_name}}::{{ds.module_name|upper}}_MIN_ADDR_WIDTH);
|
||||
|
||||
assert_bad_data_width: assert($bits({{cpuif.signal("WDATA")}}) == {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH)
|
||||
else $error("WDATA width %0d != DATA_WIDTH %0d",
|
||||
$bits({{cpuif.signal("WDATA")}}), {{ds.package_name}}::{{ds.module_name|upper}}_DATA_WIDTH);
|
||||
end
|
||||
|
||||
// Simple handshake sanity (one-cycle implication; relax/adjust as needed)
|
||||
assert_rd_resp_enc: assert property (@(posedge {{cpuif.signal("ACLK")}})
|
||||
{{cpuif.signal("RVALID")}} |-> (^{{cpuif.signal("RRESP")}} !== 1'bx))
|
||||
else $error("RRESP must be a legal AXI response when RVALID is high");
|
||||
|
||||
assert_wr_resp_enc: assert property (@(posedge {{cpuif.signal("ACLK")}})
|
||||
{{cpuif.signal("BVALID")}} |-> (^{{cpuif.signal("BRESP")}} !== 1'bx))
|
||||
else $error("BRESP must be a legal AXI response when BVALID is high");
|
||||
`endif
|
||||
{% endif -%}
|
||||
|
||||
// Max Outstanding Transactions: {{cpuif.max_outstanding}}
|
||||
logic [{{clog2(cpuif.max_outstanding+1)-1}}:0] axil_n_in_flight;
|
||||
logic axil_prev_was_rd;
|
||||
logic axil_arvalid;
|
||||
logic [{{cpuif.addr_width-1}}:0] axil_araddr;
|
||||
logic axil_ar_accept;
|
||||
logic axil_awvalid;
|
||||
logic [{{cpuif.addr_width-1}}:0] axil_awaddr;
|
||||
logic axil_wvalid;
|
||||
logic [{{cpuif.data_width-1}}:0] axil_wdata;
|
||||
logic [{{cpuif.data_width_bytes-1}}:0] axil_wstrb;
|
||||
logic axil_aw_accept;
|
||||
logic axil_resp_acked;
|
||||
|
||||
// Transaction request acceptance
|
||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||
axil_prev_was_rd <= '0;
|
||||
axil_arvalid <= '0;
|
||||
axil_araddr <= '0;
|
||||
axil_awvalid <= '0;
|
||||
axil_awaddr <= '0;
|
||||
axil_wvalid <= '0;
|
||||
axil_wdata <= '0;
|
||||
axil_wstrb <= '0;
|
||||
axil_n_in_flight <= '0;
|
||||
end else begin
|
||||
// AR* acceptance register
|
||||
if(axil_ar_accept) begin
|
||||
axil_prev_was_rd <= '1;
|
||||
axil_arvalid <= '0;
|
||||
end
|
||||
if({{cpuif.signal("arvalid")}} && {{cpuif.signal("arready")}}) begin
|
||||
axil_arvalid <= '1;
|
||||
axil_araddr <= {{cpuif.signal("araddr")}};
|
||||
end
|
||||
assign cpuif_req = {{cpuif.signal("AWVALID")}} | {{cpuif.signal("ARVALID")}};
|
||||
assign cpuif_wr_en = {{cpuif.signal("AWVALID")}} & {{cpuif.signal("WVALID")}};
|
||||
assign cpuif_rd_en = {{cpuif.signal("ARVALID")}};
|
||||
|
||||
// AW* & W* acceptance registers
|
||||
if(axil_aw_accept) begin
|
||||
axil_prev_was_rd <= '0;
|
||||
axil_awvalid <= '0;
|
||||
axil_wvalid <= '0;
|
||||
end
|
||||
if({{cpuif.signal("awvalid")}} && {{cpuif.signal("awready")}}) begin
|
||||
axil_awvalid <= '1;
|
||||
axil_awaddr <= {{cpuif.signal("awaddr")}};
|
||||
end
|
||||
if({{cpuif.signal("wvalid")}} && {{cpuif.signal("wready")}}) begin
|
||||
axil_wvalid <= '1;
|
||||
axil_wdata <= {{cpuif.signal("wdata")}};
|
||||
axil_wstrb <= {{cpuif.signal("wstrb")}};
|
||||
end
|
||||
assign cpuif_wr_addr = {{cpuif.signal("AWADDR")}};
|
||||
assign cpuif_rd_addr = {{cpuif.signal("ARADDR")}};
|
||||
|
||||
// Keep track of in-flight transactions
|
||||
if((axil_ar_accept || axil_aw_accept) && !axil_resp_acked) begin
|
||||
axil_n_in_flight <= axil_n_in_flight + 1'b1;
|
||||
end else if(!(axil_ar_accept || axil_aw_accept) && axil_resp_acked) begin
|
||||
axil_n_in_flight <= axil_n_in_flight - 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
assign cpuif_wr_data = {{cpuif.signal("WDATA")}};
|
||||
assign cpuif_wr_byte_en = {{cpuif.signal("WSTRB")}};
|
||||
|
||||
always_comb begin
|
||||
{{cpuif.signal("arready")}} = (!axil_arvalid || axil_ar_accept);
|
||||
{{cpuif.signal("awready")}} = (!axil_awvalid || axil_aw_accept);
|
||||
{{cpuif.signal("wready")}} = (!axil_wvalid || axil_aw_accept);
|
||||
end
|
||||
//
|
||||
// Return paths back to AXI master from generic cpuif_*
|
||||
// Read: ack=RVALID, err=RRESP[1] (SLVERR/DECERR), data=RDATA
|
||||
//
|
||||
assign {{cpuif.signal("RDATA")}} = cpuif_rd_data;
|
||||
assign {{cpuif.signal("RVALID")}} = cpuif_rd_ack;
|
||||
assign {{cpuif.signal("RRESP")}} = (cpuif_rd_err | cpuif_rd_sel.cpuif_err | cpuif_wr_sel.cpuif_err) ? 2'b10 : 2'b00;
|
||||
|
||||
// Request dispatch
|
||||
always_comb begin
|
||||
cpuif_wr_data = axil_wdata;
|
||||
for(int i=0; i<{{cpuif.data_width_bytes}}; i++) begin
|
||||
cpuif_wr_biten[i*8 +: 8] = {8{axil_wstrb[i]}};
|
||||
end
|
||||
cpuif_req = '0;
|
||||
cpuif_req_is_wr = '0;
|
||||
cpuif_addr = '0;
|
||||
axil_ar_accept = '0;
|
||||
axil_aw_accept = '0;
|
||||
// Write: ack=BVALID, err=BRESP[1]
|
||||
assign {{cpuif.signal("BVALID")}} = cpuif_wr_ack;
|
||||
assign {{cpuif.signal("BRESP")}} = (cpuif_wr_err | cpuif_wr_sel.cpuif_err | cpuif_rd_sel.cpuif_err) ? 2'b10 : 2'b00;
|
||||
|
||||
if(axil_n_in_flight < {{clog2(cpuif.max_outstanding+1)}}'d{{cpuif.max_outstanding}}) begin
|
||||
// Can safely issue more transactions without overwhelming response buffer
|
||||
if(axil_arvalid && !axil_prev_was_rd) begin
|
||||
cpuif_req = '1;
|
||||
cpuif_req_is_wr = '0;
|
||||
{%- if cpuif.data_width_bytes == 1 %}
|
||||
cpuif_addr = axil_araddr;
|
||||
{%- else %}
|
||||
cpuif_addr = {axil_araddr[{{cpuif.addr_width-1}}:{{clog2(cpuif.data_width_bytes)}}], {{clog2(cpuif.data_width_bytes)}}'b0};
|
||||
{%- endif %}
|
||||
if(!cpuif_req_stall_rd) axil_ar_accept = '1;
|
||||
end else if(axil_awvalid && axil_wvalid) begin
|
||||
cpuif_req = '1;
|
||||
cpuif_req_is_wr = '1;
|
||||
{%- if cpuif.data_width_bytes == 1 %}
|
||||
cpuif_addr = axil_awaddr;
|
||||
{%- else %}
|
||||
cpuif_addr = {axil_awaddr[{{cpuif.addr_width-1}}:{{clog2(cpuif.data_width_bytes)}}], {{clog2(cpuif.data_width_bytes)}}'b0};
|
||||
{%- endif %}
|
||||
if(!cpuif_req_stall_wr) axil_aw_accept = '1;
|
||||
end else if(axil_arvalid) begin
|
||||
cpuif_req = '1;
|
||||
cpuif_req_is_wr = '0;
|
||||
{%- if cpuif.data_width_bytes == 1 %}
|
||||
cpuif_addr = axil_araddr;
|
||||
{%- else %}
|
||||
cpuif_addr = {axil_araddr[{{cpuif.addr_width-1}}:{{clog2(cpuif.data_width_bytes)}}], {{clog2(cpuif.data_width_bytes)}}'b0};
|
||||
{%- endif %}
|
||||
if(!cpuif_req_stall_rd) axil_ar_accept = '1;
|
||||
end
|
||||
end
|
||||
end
|
||||
//--------------------------------------------------------------------------
|
||||
// Fanout CPU Bus interface signals
|
||||
//--------------------------------------------------------------------------
|
||||
{{fanout|walk(cpuif=cpuif)}}
|
||||
|
||||
|
||||
// AXI4-Lite Response Logic
|
||||
{%- if cpuif.resp_buffer_size == 1 %}
|
||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||
{{cpuif.signal("rvalid")}} <= '0;
|
||||
{{cpuif.signal("rresp")}} <= '0;
|
||||
{{cpuif.signal("rdata")}} <= '0;
|
||||
{{cpuif.signal("bvalid")}} <= '0;
|
||||
{{cpuif.signal("bresp")}} <= '0;
|
||||
end else begin
|
||||
if({{cpuif.signal("rvalid")}} && {{cpuif.signal("rready")}}) begin
|
||||
{{cpuif.signal("rvalid")}} <= '0;
|
||||
end
|
||||
|
||||
if({{cpuif.signal("bvalid")}} && {{cpuif.signal("bready")}}) begin
|
||||
{{cpuif.signal("bvalid")}} <= '0;
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
axil_resp_acked = '0;
|
||||
if({{cpuif.signal("rvalid")}} && {{cpuif.signal("rready")}}) axil_resp_acked = '1;
|
||||
if({{cpuif.signal("bvalid")}} && {{cpuif.signal("bready")}}) axil_resp_acked = '1;
|
||||
end
|
||||
|
||||
{%- else %}
|
||||
struct {
|
||||
logic is_wr;
|
||||
logic err;
|
||||
logic [{{cpuif.data_width-1}}:0] rdata;
|
||||
} axil_resp_buffer[{{roundup_pow2(cpuif.resp_buffer_size)}}];
|
||||
{%- if not is_pow2(cpuif.resp_buffer_size) %}
|
||||
// axil_resp_buffer is intentionally padded to the next power of two despite
|
||||
// only requiring {{cpuif.resp_buffer_size}} entries.
|
||||
// This is to avoid quirks in some tools that cannot handle indexing into a non-power-of-2 array.
|
||||
// Unused entries are expected to be optimized away
|
||||
{% endif %}
|
||||
|
||||
logic [{{clog2(cpuif.resp_buffer_size)}}:0] axil_resp_wptr;
|
||||
logic [{{clog2(cpuif.resp_buffer_size)}}:0] axil_resp_rptr;
|
||||
|
||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||
for(int i=0; i<{{cpuif.resp_buffer_size}}; i++) begin
|
||||
axil_resp_buffer[i].is_wr <= '0;
|
||||
axil_resp_buffer[i].err <= '0;
|
||||
axil_resp_buffer[i].rdata <= '0;
|
||||
end
|
||||
axil_resp_wptr <= '0;
|
||||
axil_resp_rptr <= '0;
|
||||
end else begin
|
||||
// Store responses in buffer until AXI response channel accepts them
|
||||
if(cpuif_rd_ack || cpuif_wr_ack) begin
|
||||
if(cpuif_rd_ack) begin
|
||||
axil_resp_buffer[axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)-1}}:0]].is_wr <= '0;
|
||||
axil_resp_buffer[axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)-1}}:0]].err <= cpuif_rd_err;
|
||||
axil_resp_buffer[axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)-1}}:0]].rdata <= cpuif_rd_data;
|
||||
|
||||
end else if(cpuif_wr_ack) begin
|
||||
axil_resp_buffer[axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)-1}}:0]].is_wr <= '1;
|
||||
axil_resp_buffer[axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)-1}}:0]].err <= cpuif_wr_err;
|
||||
end
|
||||
{%- if is_pow2(cpuif.resp_buffer_size) %}
|
||||
axil_resp_wptr <= axil_resp_wptr + 1'b1;
|
||||
{%- else %}
|
||||
if(axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)-1}}:0] == {{cpuif.resp_buffer_size-1}}) begin
|
||||
axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)-1}}:0] <= '0;
|
||||
axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)}}] <= ~axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)}}];
|
||||
end else begin
|
||||
axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)-1}}:0] <= axil_resp_wptr[{{clog2(cpuif.resp_buffer_size)-1}}:0] + 1'b1;
|
||||
end
|
||||
{%- endif %}
|
||||
end
|
||||
|
||||
// Advance read pointer when acknowledged
|
||||
if(axil_resp_acked) begin
|
||||
{%- if is_pow2(cpuif.resp_buffer_size) %}
|
||||
axil_resp_rptr <= axil_resp_rptr + 1'b1;
|
||||
{%- else %}
|
||||
if(axil_resp_rptr[{{clog2(cpuif.resp_buffer_size)-1}}:0] == {{cpuif.resp_buffer_size-1}}) begin
|
||||
axil_resp_rptr[{{clog2(cpuif.resp_buffer_size)-1}}:0] <= '0;
|
||||
axil_resp_rptr[{{clog2(cpuif.resp_buffer_size)}}] <= ~axil_resp_rptr[{{clog2(cpuif.resp_buffer_size)}}];
|
||||
end else begin
|
||||
axil_resp_rptr[{{clog2(cpuif.resp_buffer_size)-1}}:0] <= axil_resp_rptr[{{clog2(cpuif.resp_buffer_size)-1}}:0] + 1'b1;
|
||||
end
|
||||
{%- endif %}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
axil_resp_acked = '0;
|
||||
{{cpuif.signal("bvalid")}} = '0;
|
||||
{{cpuif.signal("rvalid")}} = '0;
|
||||
if(axil_resp_rptr != axil_resp_wptr) begin
|
||||
if(axil_resp_buffer[axil_resp_rptr[{{clog2(cpuif.resp_buffer_size)-1}}:0]].is_wr) begin
|
||||
{{cpuif.signal("bvalid")}} = '1;
|
||||
if({{cpuif.signal("bready")}}) axil_resp_acked = '1;
|
||||
end else begin
|
||||
{{cpuif.signal("rvalid")}} = '1;
|
||||
if({{cpuif.signal("rready")}}) axil_resp_acked = '1;
|
||||
end
|
||||
end
|
||||
|
||||
{{cpuif.signal("rdata")}} = axil_resp_buffer[axil_resp_rptr[{{clog2(cpuif.resp_buffer_size)-1}}:0]].rdata;
|
||||
if(axil_resp_buffer[axil_resp_rptr[{{clog2(cpuif.resp_buffer_size)-1}}:0]].err) begin
|
||||
{{cpuif.signal("bresp")}} = 2'b10;
|
||||
{{cpuif.signal("rresp")}} = 2'b10;
|
||||
end else begin
|
||||
{{cpuif.signal("bresp")}} = 2'b00;
|
||||
{{cpuif.signal("rresp")}} = 2'b00;
|
||||
end
|
||||
end
|
||||
{%- endif %}
|
||||
//--------------------------------------------------------------------------
|
||||
// Fanin CPU Bus interface signals
|
||||
//--------------------------------------------------------------------------
|
||||
{{fanin|walk(cpuif=cpuif)}}
|
||||
Reference in New Issue
Block a user