feat: Add support for OBI protocol (#158)
Signed-off-by: Daniel Keller <daniel.kellermartinez@csem.ch>
This commit is contained in:
committed by
Alex Mykyta
parent
18cf2aabc7
commit
aa9a21046d
@@ -7,7 +7,7 @@ from peakrdl.config import schema
|
|||||||
from peakrdl.plugins.entry_points import get_entry_points
|
from peakrdl.plugins.entry_points import get_entry_points
|
||||||
|
|
||||||
from .exporter import RegblockExporter
|
from .exporter import RegblockExporter
|
||||||
from .cpuif import CpuifBase, apb3, apb4, axi4lite, passthrough, avalon
|
from .cpuif import CpuifBase, apb3, apb4, axi4lite, passthrough, avalon, obi
|
||||||
from .udps import ALL_UDPS
|
from .udps import ALL_UDPS
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -38,6 +38,8 @@ class Exporter(ExporterSubcommandPlugin):
|
|||||||
"axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened,
|
"axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened,
|
||||||
"avalon-mm": avalon.Avalon_Cpuif,
|
"avalon-mm": avalon.Avalon_Cpuif,
|
||||||
"avalon-mm-flat": avalon.Avalon_Cpuif_flattened,
|
"avalon-mm-flat": avalon.Avalon_Cpuif_flattened,
|
||||||
|
"obi": obi.OBI_Cpuif,
|
||||||
|
"obi-flat": obi.OBI_Cpuif_flattened,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Load any cpuifs specified via entry points
|
# Load any cpuifs specified via entry points
|
||||||
|
|||||||
56
src/peakrdl_regblock/cpuif/obi/__init__.py
Normal file
56
src/peakrdl_regblock/cpuif/obi/__init__.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from ..base import CpuifBase
|
||||||
|
|
||||||
|
class OBI_Cpuif(CpuifBase):
|
||||||
|
template_path = "obi_tmpl.sv"
|
||||||
|
is_interface = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port_declaration(self) -> str:
|
||||||
|
return "obi_intf.subordinate obi"
|
||||||
|
|
||||||
|
def signal(self, name: str) -> str:
|
||||||
|
return "obi." + name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def regblock_latency(self) -> int:
|
||||||
|
return max(self.exp.ds.min_read_latency, self.exp.ds.min_write_latency)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_outstanding(self) -> int:
|
||||||
|
"""
|
||||||
|
OBI supports multiple outstanding transactions.
|
||||||
|
Best performance when max outstanding is design latency + 1.
|
||||||
|
"""
|
||||||
|
return self.regblock_latency + 1
|
||||||
|
|
||||||
|
|
||||||
|
class OBI_Cpuif_flattened(OBI_Cpuif):
|
||||||
|
is_interface = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port_declaration(self) -> str:
|
||||||
|
lines = [
|
||||||
|
# OBI Request Channel (A)
|
||||||
|
"input wire " + self.signal("req"),
|
||||||
|
f"input wire [{self.addr_width-1}:0] " + self.signal("addr"),
|
||||||
|
"input wire " + self.signal("we"),
|
||||||
|
f"input wire [{self.data_width//8-1}:0] " + self.signal("be"),
|
||||||
|
f"input wire [{self.data_width-1}:0] " + self.signal("wdata"),
|
||||||
|
f"input wire [{self.id_width-1}:0] " + self.signal("aid"),
|
||||||
|
|
||||||
|
# OBI Response Channel (R)
|
||||||
|
"output logic " + self.signal("gnt"),
|
||||||
|
"output logic " + self.signal("rvalid"),
|
||||||
|
f"output logic [{self.data_width-1}:0] " + self.signal("rdata"),
|
||||||
|
f"output logic [{self.id_width-1}:0] " + self.signal("rid"),
|
||||||
|
"output logic " + self.signal("err"),
|
||||||
|
"input wire " + self.signal("rready"),
|
||||||
|
]
|
||||||
|
return ",\n".join(lines)
|
||||||
|
|
||||||
|
def signal(self, name: str) -> str:
|
||||||
|
return "obi_" + name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id_width(self) -> int:
|
||||||
|
return 1 # Default ID width
|
||||||
91
src/peakrdl_regblock/cpuif/obi/obi_tmpl.sv
Normal file
91
src/peakrdl_regblock/cpuif/obi/obi_tmpl.sv
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
{%- if cpuif.is_interface -%}
|
||||||
|
`ifndef SYNTHESIS
|
||||||
|
initial begin
|
||||||
|
assert_bad_addr_width: assert($bits({{cpuif.signal("addr")}}) >= {{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("addr")}}), {{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
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
// OBI Interface Implementation
|
||||||
|
// This register block acts as an OBI subordinate
|
||||||
|
|
||||||
|
localparam int unsigned DATA_WIDTH = {{ds.package_name}}::{{ds.module_name.upper()}}_DATA_WIDTH;
|
||||||
|
localparam int unsigned BYTES = DATA_WIDTH/8;
|
||||||
|
|
||||||
|
// State & holding regs
|
||||||
|
logic is_active; // A request is being served (not yet fully responded)
|
||||||
|
logic gnt_q; // one-cycle grant for A-channel
|
||||||
|
logic rsp_pending; // response ready but not yet accepted by manager
|
||||||
|
logic [DATA_WIDTH-1:0] rsp_rdata_q;
|
||||||
|
logic rsp_err_q;
|
||||||
|
logic [$bits({{cpuif.signal("aid")}})-1:0] rid_q;
|
||||||
|
|
||||||
|
// Latch AID on accept to echo back the response
|
||||||
|
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||||
|
if ({{get_resetsignal(cpuif.reset)}}) begin
|
||||||
|
is_active <= 1'b0;
|
||||||
|
gnt_q <= 1'b0;
|
||||||
|
rsp_pending <= 1'b0;
|
||||||
|
rsp_rdata_q <= '0;
|
||||||
|
rsp_err_q <= 1'b0;
|
||||||
|
rid_q <= '0;
|
||||||
|
|
||||||
|
cpuif_req <= '0;
|
||||||
|
cpuif_req_is_wr <= '0;
|
||||||
|
cpuif_addr <= '0;
|
||||||
|
cpuif_wr_data <= '0;
|
||||||
|
cpuif_wr_biten <= '0;
|
||||||
|
end else begin
|
||||||
|
// defaults
|
||||||
|
cpuif_req <= 1'b0;
|
||||||
|
gnt_q <= {{cpuif.signal("req")}} & ~is_active;
|
||||||
|
|
||||||
|
// Accept new request when idle
|
||||||
|
if (~is_active) begin
|
||||||
|
if ({{cpuif.signal("req")}}) begin
|
||||||
|
is_active <= 1'b1;
|
||||||
|
cpuif_req <= 1'b1;
|
||||||
|
cpuif_req_is_wr <= {{cpuif.signal("we")}};
|
||||||
|
cpuif_addr <= {{cpuif.signal("addr")}};
|
||||||
|
cpuif_wr_data <= {{cpuif.signal("wdata")}};
|
||||||
|
rid_q <= {{cpuif.signal("aid")}};
|
||||||
|
for (int i = 0; i < BYTES; i++) begin
|
||||||
|
cpuif_wr_biten[i*8 +: 8] <= {8{ {{cpuif.signal("be")}}[i] }};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Capture response
|
||||||
|
if (is_active && (cpuif_rd_ack || cpuif_wr_ack)) begin
|
||||||
|
rsp_pending <= 1'b1;
|
||||||
|
rsp_rdata_q <= cpuif_rd_data;
|
||||||
|
rsp_err_q <= cpuif_rd_err | cpuif_wr_err;
|
||||||
|
// NOTE: Keep 'is_active' asserted until the external R handshake completes
|
||||||
|
end
|
||||||
|
|
||||||
|
// Complete external R-channel handshake only if manager ready
|
||||||
|
if (rsp_pending && {{cpuif.signal("rvalid")}} && {{cpuif.signal("rready")}}) begin
|
||||||
|
rsp_pending <= 1'b0;
|
||||||
|
is_active <= 1'b0; // free to accept the next request
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// R-channel outputs (held stable while rsp_pending=1)
|
||||||
|
assign {{cpuif.signal("rvalid")}} = rsp_pending;
|
||||||
|
assign {{cpuif.signal("rdata")}} = rsp_rdata_q;
|
||||||
|
assign {{cpuif.signal("err")}} = rsp_err_q;
|
||||||
|
assign {{cpuif.signal("rid")}} = rid_q;
|
||||||
|
|
||||||
|
// A-channel grant (registered one-cycle pulse when we accept a request)
|
||||||
|
assign {{cpuif.signal("gnt")}} = gnt_q;
|
||||||
|
|
||||||
|
// If OBI config RReady is disabled, tie it high in the top-level/TB.
|
||||||
|
// `ifndef SYNTHESIS
|
||||||
|
// initial begin
|
||||||
|
// if (0) $display("RReady supported; tie high if unused.");
|
||||||
|
// end
|
||||||
|
// `endif
|
||||||
Reference in New Issue
Block a user