Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ sources:
- src/apb_to_reg.sv
- src/axi_lite_to_reg.sv
- src/axi_to_reg_v2.sv
- src/obi_to_reg.sv
- src/periph_to_reg.sv
- src/reg_cdc.sv
- src/reg_cut.sv
Expand Down
69 changes: 69 additions & 0 deletions src/obi_to_reg.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2025 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

/**
* Translates OBI to register_interface,
* ignores the r_optional and a_optional fields
*/
module obi_to_reg #(
parameter int unsigned DATA_WIDTH = 32,
parameter int unsigned ID_WIDTH = 0,

parameter type obi_req_t = logic,
parameter type obi_rsp_t = logic,

parameter type reg_req_t = logic,
parameter type reg_rsp_t = logic
) (
input logic clk_i,
input logic rst_ni,

input obi_req_t obi_req_i,
output obi_rsp_t obi_rsp_o,

output reg_req_t reg_req_o,
input reg_rsp_t reg_rsp_i
);
logic req_q, req_d;
logic [ID_WIDTH-1:0] aid_q, aid_d;
logic [DATA_WIDTH-1:0] rdata_q, rdata_d;
logic error_q, error_d;

assign req_d = obi_req_i.req;
assign aid_d = obi_req_i.a.aid;
assign rdata_d = reg_rsp_i.rdata;
assign error_d = reg_rsp_i.error;

always_ff @(posedge clk_i or negedge rst_ni) begin : proc_seq
if (!rst_ni) begin
req_q <= '0;
rdata_q <= '0;
aid_q <= '0;
error_q <= '0;
end else begin
req_q <= req_d;
rdata_q <= rdata_d;
aid_q <= aid_d;
error_q <= error_d;
end
end

assign reg_req_o.valid = obi_req_i.req;
assign reg_req_o.addr = obi_req_i.a.addr;
assign reg_req_o.write = obi_req_i.a.we;
assign reg_req_o.wdata = obi_req_i.a.wdata;
assign reg_req_o.wstrb = obi_req_i.a.be;

assign obi_rsp_o.gnt = obi_req_i.req & reg_rsp_i.ready;
assign obi_rsp_o.rvalid = req_q;
assign obi_rsp_o.r.rdata = rdata_q;
assign obi_rsp_o.r.rid = aid_q;
assign obi_rsp_o.r.err = error_q;
endmodule
1 change: 1 addition & 0 deletions src_files.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ register_interface:
- src/apb_to_reg.sv
- src/axi_lite_to_reg.sv
- src/axi_to_reg_v2.sv
- src/obi_to_reg.sv
- src/periph_to_reg.sv
- src/reg_cdc.sv
- src/reg_cut.sv
Expand Down
12 changes: 12 additions & 0 deletions vendor/lowrisc_opentitan/util/reggen/bits.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ def __init__(self, msb: int, lsb: int):
def bitmask(self) -> int:
return (1 << (self.msb + 1)) - (1 << self.lsb)

def bytemask(self) -> int:
bytemask = 0

bitmask = self.bitmask()
i = 0
while bitmask >= 2**(i*8):
if bitmask & (0b1111_1111 << i*8):
bytemask |= 1 << i
i += 1

return bytemask

def width(self) -> int:
return 1 + self.msb - self.lsb

Expand Down
24 changes: 22 additions & 2 deletions vendor/lowrisc_opentitan/util/reggen/reg_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,14 @@ def add_raw(self, where: str, raw: object) -> None:
'reserved': self._handle_reserved,
'skipto': self._handle_skipto,
'window': self._handle_window,
'multireg': self._handle_multireg
'multireg': self._handle_multireg,
'packed': self._handle_packed
}

entry_type = 'register'
entry_body = entry # type: object

for t in ['reserved', 'skipto', 'window', 'multireg']:
for t in ['reserved', 'skipto', 'window', 'multireg', 'packed']:
t_body = entry.get(t)
if t_body is not None:
# Special entries look like { window: { ... } }, so if we
Expand Down Expand Up @@ -207,6 +208,25 @@ def _handle_multireg(self, where: str, body: object) -> None:
self.entries.append(mr)
self.offset = mr.next_offset(self._addrsep)

def _handle_packed(self, where: str, body: object) -> None:
reg_bodies = check_list(body, 'packed')
local_offset = 0
for i, body in enumerate(reg_bodies):
reg = Register.from_raw(self._reg_width, self.offset, self._params, body)
if i < len(reg_bodies) - 1:
reg.doesnt_increment_offset = True

byte_width = (reg.get_width() + 7) // 8 * 8

for field in reg.fields:
field.bits = field.bits.make_translated(local_offset)

local_offset += byte_width
self.add_register(reg)

if local_offset > self._reg_width:
raise ValueError(f'Packed register is larger than regwidth ({local_offset} > {self._reg_width})')

def add_register(self, reg: Register) -> None:
assert reg.offset == self.offset

Expand Down
29 changes: 17 additions & 12 deletions vendor/lowrisc_opentitan/util/reggen/reg_pkg.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -290,24 +290,29 @@ value = "{}'h {:x}".format(aw, r.offset)
% endfor
} ${lpfx}_id_e;

// Register width information to check illegal writes${for_iface}
parameter logic [3:0] ${upfx}_PERMIT [${len(rb.flat_regs)}] = '{
// Register bytemaks used to see if a register is to be written to ${for_iface}
parameter logic [3:0] ${upfx}_BYTEMASK [${len(rb.flat_regs)}] = '{
% for i, r in enumerate(rb.flat_regs):
<%
index_str = "{}".format(i).rjust(idx_len)
width = r.get_width()
if width > 24:
mask = '1111'
elif width > 16:
mask = '0111'
elif width > 8:
mask = '0011'
else:
mask = '0001'
mask = "4'b {:04b}".format(r.bytemask())

comma = ',' if i < len(rb.flat_regs) - 1 else ' '
%>\
4'b ${mask}${comma} // index[${index_str}] ${ublock}_${r.name.upper()}
${mask}${comma} // index[${index_str}] ${ublock}_${r.name.upper()}
% endfor
};

// Register boudary crossing infromation to make sure we don't write to half of a field${for_iface}
parameter logic [2:0] ${upfx}_DISALLOWED_BOUNDARY_CROSSINGS [${len(rb.flat_regs)}] = '{
% for i, r in enumerate(rb.flat_regs):
<%
index_str = "{}".format(i).rjust(idx_len)
mask = "3'b " + "{:03b}".format(r.crossed_byte_boundaries())[-3:]

comma = ',' if i < len(rb.flat_regs) - 1 else ' '
%>\
${mask}${comma} // index[${index_str}] ${ublock}_${r.name.upper()}
% endfor
};
% endif
Expand Down
44 changes: 20 additions & 24 deletions vendor/lowrisc_opentitan/util/reggen/reg_top.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -438,19 +438,21 @@ ${finst_gen(f, finst_name, fsig_name, r.hwext, r.regwen, r.shadowed)}
always_comb begin
addr_hit = '0;
% for i,r in enumerate(regs_flat):
addr_hit[${"{}".format(i).rjust(max_regs_char)}] = (reg_addr == ${ublock}_${r.name.upper()}_OFFSET);
addr_hit[${"{}".format(i).rjust(max_regs_char)}] = reg_addr == ${ublock}_${r.name.upper()}_OFFSET;
% endfor
end

assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ;

% if regs_flat:
<%
# We want to signal wr_err if reg_be (the byte enable signal) is true for
# any bytes that aren't supported by a register. That's true if a
# addr_hit[i] and a bit is set in reg_be but not in *_PERMIT[i].
# We want to signal wr_err if reg_be wants to write to only part of a field.
# This is the case if the byte enable has a border (calculated by `reg_be ^ (reg_be >> 1)`)
# at a bit where a field is crossing (DISALLOWED_BOUNDARY_CROSSINGS)
# e.g. reg_be `0b0100` has a border at position 2 and 3 (-> `0b110`) and is not allowed to write
# to a field with bytemask `0b1100`, which disallows a border at bit position 3 (`0b100`)

wr_err_terms = ['(addr_hit[{idx}] & (|({mod}_PERMIT[{idx}] & ~reg_be)))'
wr_err_terms = ['(addr_hit[{idx}] & (|((reg_be ^ (reg_be >> 1)) & {mod}_DISALLOWED_BOUNDARY_CROSSINGS[{idx}])))'
.format(idx=str(i).rjust(max_regs_char),
mod=u_mod_base)
for i in range(len(regs_flat))]
Expand Down Expand Up @@ -478,26 +480,21 @@ ${we_gen(f, r.name.lower() + "_" + f.name.lower(), r.hwext, r.shadowed, i)}\
// Read data return
always_comb begin
reg_rdata_next = '0;
unique case (1'b1)
% for i, r in enumerate(regs_flat):
% if len(r.fields) == 1:
addr_hit[${i}]: begin
% for i, r in enumerate(regs_flat):
% if len(r.fields) == 1:
if (addr_hit[${i}]) begin
${rdata_gen(r.fields[0], r.name.lower())}\
end
end

% else:
addr_hit[${i}]: begin
% for f in r.fields:
% else:
if (addr_hit[${i}]) begin
% for f in r.fields:
${rdata_gen(f, r.name.lower() + "_" + f.name.lower())}\
% endfor
end
% endfor
end

% endif
% endfor
default: begin
reg_rdata_next = '1;
end
endcase
% endif
% endfor
end
% endif

Expand Down Expand Up @@ -528,8 +525,6 @@ ${rdata_gen(f, r.name.lower() + "_" + f.name.lower())}\
// property by mistake
//`ASSUME(reqParity, tl_reg_h2d.a_valid |-> tl_reg_h2d.a_user.chk_en == tlul_pkg::CheckDis)
% endif
`ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit))

% endif
endmodule

Expand Down Expand Up @@ -770,11 +765,12 @@ ${bits.msb}\
needs_we = field.swaccess.allows_write()
needs_re = (field.swaccess.allows_read() and hwext) or shadowed
space = '\n' if needs_we or needs_re else ''
bytemask = "4'b {:04b}".format(field.bits.bytemask())
%>\
${space}\
% if needs_we:
% if field.swaccess.swrd() != SwRdAccess.RC:
assign ${sig_name}_we = addr_hit[${idx}] & reg_we & !reg_error;
assign ${sig_name}_we = addr_hit[${idx}] & reg_we & !reg_error & (|(${bytemask} & reg_be));
assign ${sig_name}_wd = reg_wdata[${str_bits_sv(field.bits)}];
% else:
## Generate WE based on read request, read should clear
Expand Down
36 changes: 33 additions & 3 deletions vendor/lowrisc_opentitan/util/reggen/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def __init__(self,
shadowed: bool,
fields: List[Field],
update_err_alert: Optional[str],
storage_err_alert: Optional[str]):
storage_err_alert: Optional[str],
doesnt_increment_offset: bool):
super().__init__(offset)
self.name = name
self.desc = desc
Expand Down Expand Up @@ -173,6 +174,7 @@ def __init__(self,

self.update_err_alert = update_err_alert
self.storage_err_alert = storage_err_alert
self.doesnt_increment_offset = doesnt_increment_offset

@staticmethod
def from_raw(reg_width: int,
Expand Down Expand Up @@ -261,9 +263,12 @@ def from_raw(reg_width: int,
return Register(offset, name, desc, swaccess, hwaccess,
hwext, hwqe, hwre, regwen,
tags, resval, shadowed, fields,
update_err_alert, storage_err_alert)
update_err_alert, storage_err_alert, False)

def next_offset(self, addrsep: int) -> int:
if self.doesnt_increment_offset:
return self.offset

return self.offset + addrsep

def sw_readable(self) -> bool:
Expand All @@ -285,6 +290,30 @@ def get_field_list(self) -> List[Field]:
def is_homogeneous(self) -> bool:
return len(self.fields) == 1

def crossed_byte_boundaries(self) -> int:
'''
Returns the byte boundaries that are crossed by a field contained in this register.
e.g. `fields: [ { bits: "23:16" }, { bits: "15:0" } ]`
crosses the third and first byte boundary, so it returns 0b101.
This is used to determine whether a write using bytestrobe is valid.
'''
boundaries = 0

for field in self.fields:
bytemask = field.bits.bytemask()
boundaries |= bytemask & (bytemask >> 1)

return boundaries

def bytemask(self) -> int:
bytemask = 0

for field in self.fields:
bytemask |= field.bits.bytemask()

return bytemask


def get_width(self) -> int:
'''Get the width of the fields in the register in bits

Expand Down Expand Up @@ -350,7 +379,7 @@ def make_multi(self,
self.swaccess, self.hwaccess,
self.hwext, self.hwqe, self.hwre, new_regwen,
self.tags, new_resval, self.shadowed, new_fields,
self.update_err_alert, self.storage_err_alert)
self.update_err_alert, self.storage_err_alert, False)

def _asdict(self) -> Dict[str, object]:
rd = {
Expand All @@ -364,6 +393,7 @@ def _asdict(self) -> Dict[str, object]:
'hwre': str(self.hwre),
'tags': self.tags,
'shadowed': str(self.shadowed),
'doesnt_increment_offset': str(self.doesnt_increment_offset),
}
if self.regwen is not None:
rd['regwen'] = self.regwen
Expand Down