Add support for cpuif that have write strobes

This commit is contained in:
Alex Mykyta
2022-09-12 21:32:52 -07:00
parent 4bee895c71
commit 6e4246a2cc
14 changed files with 152 additions and 8 deletions

View File

@@ -34,6 +34,10 @@ cpuif_wr_data
Data to be written for the write transfer. This signal is ignored for read Data to be written for the write transfer. This signal is ignored for read
transfers. transfers.
cpuif_wr_biten
Active-high bit-level write-enable strobes.
Only asserted bit positions will change the register value during a write transfer.
cpuif_req_stall_rd cpuif_req_stall_rd
If asserted, and the next pending request is a read operation, then the If asserted, and the next pending request is a read operation, then the
transfer will not be accepted until this signal is deasserted. transfer will not be accepted until this signal is deasserted.

View File

@@ -173,6 +173,10 @@ X Do not allow unaligned addresses
! Error if a property references a non-signal component, or property reference from ! Error if a property references a non-signal component, or property reference from
outside the export hierarchy outside the export hierarchy
! Error if a property is a reference to something that is external, or enclosed
in an external component.
Limit this check to child nodes inside the export hierarchy
! Add warning for sticky race condition ! Add warning for sticky race condition
stickybit and other similar situations generally should use hw precedence. stickybit and other similar situations generally should use hw precedence.
Emit a warning as appropriate Emit a warning as appropriate

View File

@@ -28,6 +28,7 @@ always_ff {{get_always_ff_event(cpuif.reset)}} begin
end end
end end
end end
assign cpuif_wr_biten = '1;
// Response // Response
assign {{cpuif.signal("pready")}} = cpuif_rd_ack | cpuif_wr_ack; assign {{cpuif.signal("pready")}} = cpuif_rd_ack | cpuif_wr_ack;

View File

@@ -8,6 +8,7 @@ logic axil_awvalid;
logic [{{cpuif.addr_width-1}}:0] axil_awaddr; logic [{{cpuif.addr_width-1}}:0] axil_awaddr;
logic axil_wvalid; logic axil_wvalid;
logic [{{cpuif.data_width-1}}:0] axil_wdata; logic [{{cpuif.data_width-1}}:0] axil_wdata;
logic [{{cpuif.data_width-1}}:0] axil_wstrb;
logic axil_aw_accept; logic axil_aw_accept;
logic axil_resp_acked; logic axil_resp_acked;
@@ -21,6 +22,7 @@ always_ff {{get_always_ff_event(cpuif.reset)}} begin
axil_awaddr <= '0; axil_awaddr <= '0;
axil_wvalid <= '0; axil_wvalid <= '0;
axil_wdata <= '0; axil_wdata <= '0;
axil_wstrb <= '0;
axil_n_in_flight <= '0; axil_n_in_flight <= '0;
end else begin end else begin
// AR* acceptance register // AR* acceptance register
@@ -46,6 +48,7 @@ always_ff {{get_always_ff_event(cpuif.reset)}} begin
if({{cpuif.signal("wvalid")}} && {{cpuif.signal("wready")}}) begin if({{cpuif.signal("wvalid")}} && {{cpuif.signal("wready")}}) begin
axil_wvalid <= '1; axil_wvalid <= '1;
axil_wdata <= {{cpuif.signal("wdata")}}; axil_wdata <= {{cpuif.signal("wdata")}};
axil_wstrb <= {{cpuif.signal("wstrb")}};
end end
// Keep track of in-flight transactions // Keep track of in-flight transactions
@@ -66,6 +69,9 @@ end
// Request dispatch // Request dispatch
always_comb begin always_comb begin
cpuif_wr_data = axil_wdata; 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 = '0;
cpuif_req_is_wr = '0; cpuif_req_is_wr = '0;
cpuif_addr = '0; cpuif_addr = '0;

View File

@@ -10,6 +10,7 @@ class PassthroughCpuif(CpuifBase):
"input wire s_cpuif_req_is_wr", "input wire s_cpuif_req_is_wr",
f"input wire [{self.addr_width-1}:0] s_cpuif_addr", f"input wire [{self.addr_width-1}:0] s_cpuif_addr",
f"input wire [{self.data_width-1}:0] s_cpuif_wr_data", f"input wire [{self.data_width-1}:0] s_cpuif_wr_data",
f"input wire [{self.data_width-1}:0] s_cpuif_wr_biten",
"output wire s_cpuif_req_stall_wr", "output wire s_cpuif_req_stall_wr",
"output wire s_cpuif_req_stall_rd", "output wire s_cpuif_req_stall_rd",
"output wire s_cpuif_rd_ack", "output wire s_cpuif_rd_ack",

View File

@@ -2,6 +2,7 @@ assign cpuif_req = s_cpuif_req;
assign cpuif_req_is_wr = s_cpuif_req_is_wr; assign cpuif_req_is_wr = s_cpuif_req_is_wr;
assign cpuif_addr = s_cpuif_addr; assign cpuif_addr = s_cpuif_addr;
assign cpuif_wr_data = s_cpuif_wr_data; assign cpuif_wr_data = s_cpuif_wr_data;
assign cpuif_wr_biten = s_cpuif_wr_biten;
assign s_cpuif_req_stall_wr = cpuif_req_stall_wr; assign s_cpuif_req_stall_wr = cpuif_req_stall_wr;
assign s_cpuif_req_stall_rd = cpuif_req_stall_rd; assign s_cpuif_req_stall_rd = cpuif_req_stall_rd;
assign s_cpuif_rd_ack = cpuif_rd_ack; assign s_cpuif_rd_ack = cpuif_rd_ack;

View File

@@ -33,14 +33,25 @@ class _OnWrite(NextStateConditional):
value = f"decoded_wr_data[{field.high}:{field.low}]" value = f"decoded_wr_data[{field.high}:{field.low}]"
return value return value
def _wr_biten(self, field: 'FieldNode') -> str:
if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation
value = f"{{<<{{decoded_wr_biten[{field.high}:{field.low}]}}}}"
else:
value = f"decoded_wr_biten[{field.high}:{field.low}]"
return value
class WriteOneSet(_OnWrite): class WriteOneSet(_OnWrite):
comment = "SW write 1 set" comment = "SW write 1 set"
onwritetype = OnWriteType.woset onwritetype = OnWriteType.woset
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
R = self.exp.field_logic.get_storage_identifier(field) R = self.exp.field_logic.get_storage_identifier(field)
D = self._wr_data(field)
S = self._wr_biten(field)
return [ return [
f"next_c = {R} | {self._wr_data(field)};", f"next_c = {R} | ({D} & {S});",
"load_next_c = '1;", "load_next_c = '1;",
] ]
@@ -50,8 +61,10 @@ class WriteOneClear(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
R = self.exp.field_logic.get_storage_identifier(field) R = self.exp.field_logic.get_storage_identifier(field)
D = self._wr_data(field)
S = self._wr_biten(field)
return [ return [
f"next_c = {R} & ~{self._wr_data(field)};", f"next_c = {R} & ~({D} & {S});",
"load_next_c = '1;", "load_next_c = '1;",
] ]
@@ -61,8 +74,10 @@ class WriteOneToggle(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
R = self.exp.field_logic.get_storage_identifier(field) R = self.exp.field_logic.get_storage_identifier(field)
D = self._wr_data(field)
S = self._wr_biten(field)
return [ return [
f"next_c = {R} ^ {self._wr_data(field)};", f"next_c = {R} ^ ({D} & {S});",
"load_next_c = '1;", "load_next_c = '1;",
] ]
@@ -72,8 +87,10 @@ class WriteZeroSet(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
R = self.exp.field_logic.get_storage_identifier(field) R = self.exp.field_logic.get_storage_identifier(field)
D = self._wr_data(field)
S = self._wr_biten(field)
return [ return [
f"next_c = {R} | ~{self._wr_data(field)};", f"next_c = {R} | (~{D} & {S});",
"load_next_c = '1;", "load_next_c = '1;",
] ]
@@ -83,8 +100,10 @@ class WriteZeroClear(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
R = self.exp.field_logic.get_storage_identifier(field) R = self.exp.field_logic.get_storage_identifier(field)
D = self._wr_data(field)
S = self._wr_biten(field)
return [ return [
f"next_c = {R} & {self._wr_data(field)};", f"next_c = {R} & ({D} | ~{S});",
"load_next_c = '1;", "load_next_c = '1;",
] ]
@@ -94,8 +113,10 @@ class WriteZeroToggle(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
R = self.exp.field_logic.get_storage_identifier(field) R = self.exp.field_logic.get_storage_identifier(field)
D = self._wr_data(field)
S = self._wr_biten(field)
return [ return [
f"next_c = {R} ^ ~{self._wr_data(field)};", f"next_c = {R} ^ (~{D} & {S});",
"load_next_c = '1;", "load_next_c = '1;",
] ]
@@ -124,7 +145,10 @@ class Write(_OnWrite):
onwritetype = None onwritetype = None
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
R = self.exp.field_logic.get_storage_identifier(field)
D = self._wr_data(field)
S = self._wr_biten(field)
return [ return [
f"next_c = {self._wr_data(field)};", f"next_c = ({R} & ~{S}) | ({D} & {S});",
"load_next_c = '1;", "load_next_c = '1;",
] ]

View File

@@ -26,6 +26,7 @@ module {{module_name}} (
logic cpuif_req_is_wr; logic cpuif_req_is_wr;
logic [{{cpuif.addr_width-1}}:0] cpuif_addr; logic [{{cpuif.addr_width-1}}:0] cpuif_addr;
logic [{{cpuif.data_width-1}}:0] cpuif_wr_data; logic [{{cpuif.data_width-1}}:0] cpuif_wr_data;
logic [{{cpuif.data_width-1}}:0] cpuif_wr_biten;
logic cpuif_req_stall_wr; logic cpuif_req_stall_wr;
logic cpuif_req_stall_rd; logic cpuif_req_stall_rd;
@@ -84,6 +85,7 @@ module {{module_name}} (
logic decoded_req; logic decoded_req;
logic decoded_req_is_wr; logic decoded_req_is_wr;
logic [{{cpuif.data_width-1}}:0] decoded_wr_data; logic [{{cpuif.data_width-1}}:0] decoded_wr_data;
logic [{{cpuif.data_width-1}}:0] decoded_wr_biten;
always_comb begin always_comb begin
{{address_decode.get_implementation()|indent(8)}} {{address_decode.get_implementation()|indent(8)}}
@@ -93,6 +95,7 @@ module {{module_name}} (
assign decoded_req = cpuif_req_masked; assign decoded_req = cpuif_req_masked;
assign decoded_req_is_wr = cpuif_req_is_wr; assign decoded_req_is_wr = cpuif_req_is_wr;
assign decoded_wr_data = cpuif_wr_data; assign decoded_wr_data = cpuif_wr_data;
assign decoded_wr_biten = cpuif_wr_biten;
// Writes are always granted with no error response // Writes are always granted with no error response
assign cpuif_wr_ack = decoded_req & decoded_req_is_wr; assign cpuif_wr_ack = decoded_req & decoded_req_is_wr;

View File

@@ -9,6 +9,7 @@ interface passthrough_driver #(
output logic m_cpuif_req_is_wr, output logic m_cpuif_req_is_wr,
output logic [ADDR_WIDTH-1:0] m_cpuif_addr, output logic [ADDR_WIDTH-1:0] m_cpuif_addr,
output logic [DATA_WIDTH-1:0] m_cpuif_wr_data, output logic [DATA_WIDTH-1:0] m_cpuif_wr_data,
output logic [DATA_WIDTH-1:0] m_cpuif_wr_biten,
input wire m_cpuif_req_stall_wr, input wire m_cpuif_req_stall_wr,
input wire m_cpuif_req_stall_rd, input wire m_cpuif_req_stall_rd,
input wire m_cpuif_rd_ack, input wire m_cpuif_rd_ack,
@@ -27,6 +28,7 @@ interface passthrough_driver #(
output m_cpuif_req_is_wr; output m_cpuif_req_is_wr;
output m_cpuif_addr; output m_cpuif_addr;
output m_cpuif_wr_data; output m_cpuif_wr_data;
output m_cpuif_wr_biten;
input m_cpuif_req_stall_wr; input m_cpuif_req_stall_wr;
input m_cpuif_req_stall_rd; input m_cpuif_req_stall_rd;
input m_cpuif_rd_ack; input m_cpuif_rd_ack;
@@ -41,12 +43,13 @@ interface passthrough_driver #(
cb.m_cpuif_req_is_wr <= '0; cb.m_cpuif_req_is_wr <= '0;
cb.m_cpuif_addr <= '0; cb.m_cpuif_addr <= '0;
cb.m_cpuif_wr_data <= '0; cb.m_cpuif_wr_data <= '0;
cb.m_cpuif_wr_biten <= '0;
endtask endtask
semaphore txn_req_mutex = new(1); semaphore txn_req_mutex = new(1);
semaphore txn_resp_mutex = new(1); semaphore txn_resp_mutex = new(1);
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data); task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH-1:0] biten = '1);
fork fork
begin begin
// Initiate transfer // Initiate transfer
@@ -56,6 +59,7 @@ interface passthrough_driver #(
cb.m_cpuif_req_is_wr <= '1; cb.m_cpuif_req_is_wr <= '1;
cb.m_cpuif_addr <= addr; cb.m_cpuif_addr <= addr;
cb.m_cpuif_wr_data <= data; cb.m_cpuif_wr_data <= data;
cb.m_cpuif_wr_biten <= biten;
@(cb); @(cb);
while(cb.m_cpuif_req_stall_wr !== 1'b0) @(cb); while(cb.m_cpuif_req_stall_wr !== 1'b0) @(cb);
reset(); reset();

View File

@@ -3,6 +3,7 @@ wire s_cpuif_req;
wire s_cpuif_req_is_wr; wire s_cpuif_req_is_wr;
wire [{{exporter.cpuif.addr_width-1}}:0] s_cpuif_addr; wire [{{exporter.cpuif.addr_width-1}}:0] s_cpuif_addr;
wire [{{exporter.cpuif.data_width-1}}:0] s_cpuif_wr_data; wire [{{exporter.cpuif.data_width-1}}:0] s_cpuif_wr_data;
wire [{{exporter.cpuif.data_width-1}}:0] s_cpuif_wr_biten;
wire s_cpuif_req_stall_wr; wire s_cpuif_req_stall_wr;
wire s_cpuif_req_stall_rd; wire s_cpuif_req_stall_rd;
wire s_cpuif_rd_ack; wire s_cpuif_rd_ack;
@@ -20,6 +21,7 @@ passthrough_driver #(
.m_cpuif_req_is_wr(s_cpuif_req_is_wr), .m_cpuif_req_is_wr(s_cpuif_req_is_wr),
.m_cpuif_addr(s_cpuif_addr), .m_cpuif_addr(s_cpuif_addr),
.m_cpuif_wr_data(s_cpuif_wr_data), .m_cpuif_wr_data(s_cpuif_wr_data),
.m_cpuif_wr_biten(s_cpuif_wr_biten),
.m_cpuif_req_stall_wr(s_cpuif_req_stall_wr), .m_cpuif_req_stall_wr(s_cpuif_req_stall_wr),
.m_cpuif_req_stall_rd(s_cpuif_req_stall_rd), .m_cpuif_req_stall_rd(s_cpuif_req_stall_rd),
.m_cpuif_rd_ack(s_cpuif_rd_ack), .m_cpuif_rd_ack(s_cpuif_rd_ack),

View File

View File

@@ -0,0 +1,54 @@
addrmap top {
reg {
field {
sw=rw; hw=na;
onwrite = woset;
} f1[3:0] = 0x0;
field {
sw=rw; hw=na;
onwrite = woclr;
} f2[7:4] = 0xF;
field {
sw=rw; hw=na;
onwrite = wot;
} f3[11:8] = 0x0;
} r1;
reg {
field {
sw=rw; hw=na;
onwrite = wzs;
} f1[3:0] = 0x0;
field {
sw=rw; hw=na;
onwrite = wzc;
} f2[7:4] = 0xF;
field {
sw=rw; hw=na;
onwrite = wzt;
} f3[11:8] = 0x0;
} r2;
reg {
field {
sw=rw; hw=na;
onwrite = wclr;
} f1[7:0] = 0xF0;
field {
sw=rw; hw=na;
onwrite = wset;
} f2[15:8] = 0x0F;
} r3;
reg {
field {
sw=rw; hw=na;
} f3[7:0] = 0x00;
} r4;
};

View File

@@ -0,0 +1,31 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
cpuif.assert_read('h0, 'h0_F_0);
cpuif.write ('h0, 'h5_5_5, 'h3_3_3);
cpuif.assert_read('h0, 'h1_E_1);
cpuif.write ('h0, 'h5_A_A, 'h3_3_3);
cpuif.assert_read('h0, 'h0_C_3);
cpuif.assert_read('h4, 'h0_F_0);
cpuif.write ('h4, 'hA_A_A, 'h3_3_3);
cpuif.assert_read('h4, 'h1_E_1);
cpuif.write ('h4, 'hA_5_5, 'h3_3_3);
cpuif.assert_read('h4, 'h0_C_3);
cpuif.assert_read('h8, 'h0F_F0);
cpuif.write ('h8, 'h12_34, 'hFF_00);
cpuif.assert_read('h8, 'hFF_00);
cpuif.assert_read('hC, 'h00);
cpuif.write ('hC, 'hFF, 'hF0);
cpuif.assert_read('hC, 'hF0);
cpuif.write ('hC, 'h00, 'h3C);
cpuif.assert_read('hC, 'hC0);
{% endblock %}

View File

@@ -0,0 +1,9 @@
from ..lib.sim_testcase import SimTestCase
from ..lib.cpuifs.passthrough import Passthrough
class Test(SimTestCase):
cpuif = Passthrough()
def test_dut(self):
self.run_test()