| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // Register Top module auto-generated by `reggen` |
| <% |
| num_wins = len(block.wins) |
| num_wins_width = ((num_wins+1).bit_length()) - 1 |
| num_dsp = num_wins + 1 |
| params = [p for p in block.params if p["local"] == "false"] |
| max_regs_char = len("{}".format(block.get_n_regs_flat()-1)) |
| regs_flat = block.get_regs_flat() |
| %> |
| `include "prim_assert.sv" |
| |
| module ${block.name}_reg_top ${print_param(params)}( |
| input clk_i, |
| input rst_ni, |
| |
| // Below Regster interface can be changed |
| input tlul_pkg::tl_h2d_t tl_i, |
| output tlul_pkg::tl_d2h_t tl_o, |
| % if num_wins != 0: |
| |
| // Output port for window |
| output tlul_pkg::tl_h2d_t tl_win_o [${num_wins}], |
| input tlul_pkg::tl_d2h_t tl_win_i [${num_wins}], |
| |
| % endif |
| // To HW |
| % if block.get_n_bits(["q","qe","re"]): |
| output ${block.name}_reg_pkg::${block.name}_reg2hw_t reg2hw, // Write |
| % endif |
| % if block.get_n_bits(["d","de"]): |
| input ${block.name}_reg_pkg::${block.name}_hw2reg_t hw2reg, // Read |
| % endif |
| |
| // Config |
| input devmode_i // If 1, explicit error return for unmapped register access |
| ); |
| |
| import ${block.name}_reg_pkg::* ; |
| |
| localparam int AW = ${block.addr_width}; |
| localparam int DW = ${block.width}; |
| localparam int DBW = DW/8; // Byte Width |
| |
| // register signals |
| logic reg_we; |
| logic reg_re; |
| logic [AW-1:0] reg_addr; |
| logic [DW-1:0] reg_wdata; |
| logic [DBW-1:0] reg_be; |
| logic [DW-1:0] reg_rdata; |
| logic reg_error; |
| |
| logic addrmiss, wr_err; |
| |
| logic [DW-1:0] reg_rdata_next; |
| |
| tlul_pkg::tl_h2d_t tl_reg_h2d; |
| tlul_pkg::tl_d2h_t tl_reg_d2h; |
| |
| % if num_wins == 0: |
| assign tl_reg_h2d = tl_i; |
| assign tl_o = tl_reg_d2h; |
| % else: |
| tlul_pkg::tl_h2d_t tl_socket_h2d [${num_dsp}]; |
| tlul_pkg::tl_d2h_t tl_socket_d2h [${num_dsp}]; |
| |
| logic [${num_wins_width}:0] reg_steer; |
| |
| // socket_1n connection |
| assign tl_reg_h2d = tl_socket_h2d[${num_wins}]; |
| assign tl_socket_d2h[${num_wins}] = tl_reg_d2h; |
| |
| % for i,t in enumerate(block.wins): |
| assign tl_win_o[${i}] = tl_socket_h2d[${i}]; |
| assign tl_socket_d2h[${i}] = tl_win_i[${i}]; |
| % endfor |
| |
| // Create Socket_1n |
| tlul_socket_1n #( |
| .N (${num_dsp}), |
| .HReqPass (1'b1), |
| .HRspPass (1'b1), |
| .DReqPass ({${num_dsp}{1'b1}}), |
| .DRspPass ({${num_dsp}{1'b1}}), |
| .HReqDepth (4'h0), |
| .HRspDepth (4'h0), |
| .DReqDepth ({${num_dsp}{4'h0}}), |
| .DRspDepth ({${num_dsp}{4'h0}}) |
| ) u_socket ( |
| .clk_i, |
| .rst_ni, |
| .tl_h_i (tl_i), |
| .tl_h_o (tl_o), |
| .tl_d_o (tl_socket_h2d), |
| .tl_d_i (tl_socket_d2h), |
| .dev_select_i (reg_steer) |
| ); |
| |
| // Create steering logic |
| always_comb begin |
| reg_steer = ${num_dsp-1}; // Default set to register |
| |
| // TODO: Can below codes be unique case () inside ? |
| % for i,w in enumerate(block.wins): |
| % if w.limit_addr == 2**block.addr_width: |
| if (tl_i.a_address[AW-1:0] >= ${w.base_addr}) begin |
| // Exceed or meet the address range. Removed the comparison of limit addr ${"'h %x" % w.limit_addr} |
| % else: |
| if (tl_i.a_address[AW-1:0] >= ${w.base_addr} && tl_i.a_address[AW-1:0] < ${w.limit_addr}) begin |
| % endif |
| reg_steer = ${i}; |
| end |
| % endfor |
| end |
| % endif |
| |
| tlul_adapter_reg #( |
| .RegAw(AW), |
| .RegDw(DW) |
| ) u_reg_if ( |
| .clk_i, |
| .rst_ni, |
| |
| .tl_i (tl_reg_h2d), |
| .tl_o (tl_reg_d2h), |
| |
| .we_o (reg_we), |
| .re_o (reg_re), |
| .addr_o (reg_addr), |
| .wdata_o (reg_wdata), |
| .be_o (reg_be), |
| .rdata_i (reg_rdata), |
| .error_i (reg_error) |
| ); |
| |
| assign reg_rdata = reg_rdata_next ; |
| assign reg_error = (devmode_i & addrmiss) | wr_err ; |
| |
| // Define SW related signals |
| // Format: <reg>_<field>_{wd|we|qs} |
| // or <reg>_{wd|we|qs} if field == 1 or 0 |
| % for r in regs_flat: |
| % if len(r.fields) == 1: |
| <% |
| msb = r.fields[0].msb |
| lsb = r.fields[0].lsb |
| sig_name = r.name |
| f = r.fields[0] |
| swwraccess = f.swwraccess |
| swrdaccess = f.swrdaccess |
| hwext = r.hwext |
| regwen = r.regwen |
| shadowed = r.shadowed |
| %>\ |
| ${sig_gen(msb, lsb, sig_name, swwraccess, swrdaccess, hwext, regwen, shadowed)}\ |
| % else: |
| % for f in r.fields: |
| <% |
| msb = f.msb |
| lsb = f.lsb |
| sig_name = r.name + "_" + f.name |
| swwraccess = f.swwraccess |
| swrdaccess = f.swrdaccess |
| hwext = r.hwext |
| regwen = r.regwen |
| shadowed = r.shadowed |
| %>\ |
| ${sig_gen(msb, lsb, sig_name, swwraccess, swrdaccess, hwext, regwen, shadowed)}\ |
| % endfor |
| % endif |
| % endfor |
| |
| // Register instances |
| % for r in block.regs: |
| ######################## multiregister ########################### |
| % if r.is_multi_reg(): |
| <% |
| mreg_flat = r.get_regs_flat() |
| k = 0 |
| %> |
| % for sr in mreg_flat: |
| // Subregister ${k} of Multireg ${r.name} |
| // R[${sr.name}]: V(${str(sr.hwext)}) |
| % if len(sr.fields) == 1: |
| <% |
| f = sr.fields[0] |
| finst_name = sr.name |
| fsig_name = r.name + "[%d]" % k |
| msb = f.msb |
| lsb = f.lsb |
| swaccess = f.swaccess |
| swrdaccess = f.swrdaccess |
| swwraccess = f.swwraccess |
| hwaccess = f.hwaccess |
| hwqe = f.hwqe |
| hwre = f.hwre |
| hwext = sr.hwext |
| resval = f.resval |
| regwen = sr.regwen |
| shadowed = sr.shadowed |
| k = k + 1 |
| %> |
| ${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen, shadowed)} |
| % else: |
| % for f in sr.fields: |
| <% |
| finst_name = sr.name + "_" + f.name |
| if r.ishomog: |
| fsig_name = r.name + "[%d]" % k |
| k = k + 1 |
| else: |
| fsig_name = r.name + "[%d]" % k + "." + f.get_basename() |
| msb = f.msb |
| lsb = f.lsb |
| swaccess = f.swaccess |
| swrdaccess = f.swrdaccess |
| swwraccess = f.swwraccess |
| hwaccess = f.hwaccess |
| hwqe = f.hwqe |
| hwre = f.hwre |
| hwext = sr.hwext |
| resval = f.resval |
| regwen = sr.regwen |
| shadowed = sr.shadowed |
| %> |
| // F[${f.name}]: ${f.msb}:${f.lsb} |
| ${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen, shadowed)} |
| % endfor |
| <% |
| if not r.ishomog: |
| k += 1 |
| %> |
| % endif |
| ## for: mreg_flat |
| % endfor |
| ######################## register with single field ########################### |
| % elif len(r.fields) == 1: |
| // R[${r.name}]: V(${str(r.hwext)}) |
| <% |
| f = r.fields[0] |
| finst_name = r.name |
| fsig_name = r.name |
| msb = f.msb |
| lsb = f.lsb |
| swaccess = f.swaccess |
| swrdaccess = f.swrdaccess |
| swwraccess = f.swwraccess |
| hwaccess = f.hwaccess |
| hwqe = f.hwqe |
| hwre = f.hwre |
| hwext = r.hwext |
| resval = f.resval |
| regwen = r.regwen |
| shadowed = r.shadowed |
| %> |
| ${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen, shadowed)} |
| ######################## register with multiple fields ########################### |
| % else: |
| // R[${r.name}]: V(${str(r.hwext)}) |
| % for f in r.fields: |
| <% |
| finst_name = r.name + "_" + f.name |
| fsig_name = r.name + "." + f.name |
| msb = f.msb |
| lsb = f.lsb |
| swaccess = f.swaccess |
| swrdaccess = f.swrdaccess |
| swwraccess = f.swwraccess |
| hwaccess = f.hwaccess |
| hwqe = f.hwqe |
| hwre = f.hwre |
| hwext = r.hwext |
| resval = f.resval |
| regwen = r.regwen |
| shadowed = r.shadowed |
| %> |
| // F[${f.name}]: ${f.msb}:${f.lsb} |
| ${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen, shadowed)} |
| % endfor |
| % endif |
| |
| ## for: block.regs |
| % endfor |
| |
| |
| logic [${len(regs_flat)-1}:0] addr_hit; |
| always_comb begin |
| addr_hit = '0; |
| % for i,r in enumerate(regs_flat): |
| addr_hit[${"{}".format(i).rjust(max_regs_char)}] = (reg_addr == ${block.name.upper()}_${r.name.upper()}_OFFSET); |
| % endfor |
| end |
| |
| assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; |
| |
| // Check sub-word write is permitted |
| always_comb begin |
| wr_err = 1'b0; |
| % for i,r in enumerate(regs_flat): |
| <% index_str = "{}".format(i).rjust(max_regs_char) %>\ |
| if (addr_hit[${index_str}] && reg_we && (${block.name.upper()}_PERMIT[${index_str}] != (${block.name.upper()}_PERMIT[${index_str}] & reg_be))) wr_err = 1'b1 ; |
| % endfor |
| end |
| % for i, r in enumerate(regs_flat): |
| % if len(r.fields) == 1: |
| <% |
| f = r.fields[0] |
| sig_name = r.name |
| inst_name = r.name |
| msb = f.msb |
| lsb = f.lsb |
| swrdaccess = f.swrdaccess |
| swwraccess = f.swwraccess |
| hwext = r.hwext |
| shadowed = r.shadowed |
| %> |
| ${we_gen(sig_name, msb, lsb, swrdaccess, swwraccess, hwext, shadowed, i)}\ |
| % else: |
| % for f in r.fields: |
| <% |
| sig_name = r.name + "_" + f.name |
| inst_name = r.name + "." + f.name |
| msb = f.msb |
| lsb = f.lsb |
| swrdaccess = f.swrdaccess |
| swwraccess = f.swwraccess |
| hwext = r.hwext |
| shadowed = r.shadowed |
| %> |
| ${we_gen(sig_name, msb, lsb, swrdaccess, swwraccess, hwext, shadowed, i)}\ |
| % endfor |
| % endif |
| % endfor |
| |
| // 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: |
| <% |
| f = r.fields[0] |
| sig_name = r.name |
| inst_name = r.name |
| msb = f.msb |
| lsb = f.lsb |
| swrdaccess = f.swrdaccess |
| %>\ |
| addr_hit[${i}]: begin |
| ${rdata_gen(sig_name, msb, lsb, swrdaccess)}\ |
| end |
| |
| % else: |
| addr_hit[${i}]: begin |
| % for f in r.fields: |
| <% |
| sig_name = r.name + "_" + f.name |
| inst_name = r.name + "." + f.name |
| msb = f.msb |
| lsb = f.lsb |
| swrdaccess = f.swrdaccess |
| %>\ |
| ${rdata_gen(sig_name, msb, lsb, swrdaccess)}\ |
| % endfor |
| end |
| |
| % endif |
| % endfor |
| default: begin |
| reg_rdata_next = '1; |
| end |
| endcase |
| end |
| |
| // Assertions for Register Interface |
| `ASSERT_PULSE(wePulse, reg_we) |
| `ASSERT_PULSE(rePulse, reg_re) |
| |
| `ASSERT(reAfterRv, $rose(reg_re || reg_we) |=> tl_o.d_valid) |
| |
| `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) |
| |
| // this is formulated as an assumption such that the FPV testbenches do disprove this |
| // property by mistake |
| `ASSUME(reqParity, tl_reg_h2d.a_valid |-> tl_reg_h2d.a_user.parity_en == 1'b0) |
| |
| endmodule |
| <%def name="str_bits_sv(msb, lsb)">\ |
| % if msb != lsb: |
| ${msb}:${lsb}\ |
| % else: |
| ${msb}\ |
| % endif |
| </%def>\ |
| <%def name="str_arr_sv(msb, lsb)">\ |
| % if msb != lsb: |
| [${msb-lsb}:0] \ |
| % endif |
| </%def>\ |
| <%def name="sig_gen(msb, lsb, sig_name, swwraccess, swrdaccess, hwext, regwen, shadowed)">\ |
| % if swrdaccess != SwRdAccess.NONE: |
| logic ${str_arr_sv(msb, lsb)}${sig_name}_qs; |
| % endif |
| % if swwraccess != SwWrAccess.NONE: |
| logic ${str_arr_sv(msb, lsb)}${sig_name}_wd; |
| logic ${sig_name}_we; |
| % endif |
| % if (swrdaccess != SwRdAccess.NONE and hwext) or shadowed: |
| logic ${sig_name}_re; |
| % endif |
| </%def>\ |
| <%def name="finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen, shadowed)">\ |
| % if hwext: ## if hwext, instantiate prim_subreg_ext |
| prim_subreg_ext #( |
| .DW (${msb - lsb + 1}) |
| ) u_${finst_name} ( |
| % if swrdaccess != SwRdAccess.NONE: |
| .re (${finst_name}_re), |
| % else: |
| .re (1'b0), |
| % endif |
| % if swwraccess != SwWrAccess.NONE: |
| % if regwen: |
| // qualified with register enable |
| .we (${finst_name}_we & ${regwen}_qs), |
| % else: |
| .we (${finst_name}_we), |
| % endif |
| .wd (${finst_name}_wd), |
| % else: |
| .we (1'b0), |
| .wd ('0), |
| % endif |
| % if hwaccess == HwAccess.HRO: |
| .d ('0), |
| % else: |
| .d (hw2reg.${fsig_name}.d), |
| % endif |
| % if hwre or shadowed: |
| .qre (reg2hw.${fsig_name}.re), |
| % else: |
| .qre (), |
| % endif |
| % if hwaccess == HwAccess.HWO: |
| .qe (), |
| .q (), |
| % else: |
| % if hwqe: |
| .qe (reg2hw.${fsig_name}.qe), |
| % else: |
| .qe (), |
| % endif |
| .q (reg2hw.${fsig_name}.q ), |
| % endif |
| % if swrdaccess != SwRdAccess.NONE: |
| .qs (${finst_name}_qs) |
| % else: |
| .qs () |
| % endif |
| ); |
| % else: ## if not hwext, instantiate prim_subreg, prim_subreg_shadow or constant assign |
| % if hwaccess == HwAccess.NONE and swrdaccess == SwRdAccess.RD and swwraccess == SwWrAccess.NONE: |
| // constant-only read |
| assign ${finst_name}_qs = ${msb-lsb+1}'h${"%x" % resval}; |
| % else: ## not hwext not constant |
| % if not shadowed: |
| prim_subreg #( |
| % else: |
| prim_subreg_shadow #( |
| % endif |
| .DW (${msb - lsb + 1}), |
| .SWACCESS("${swaccess.name}"), |
| .RESVAL (${msb-lsb+1}'h${"%x" % resval}) |
| ) u_${finst_name} ( |
| .clk_i (clk_i ), |
| .rst_ni (rst_ni ), |
| |
| % if shadowed: |
| .re (${finst_name}_re), |
| % endif |
| % if swwraccess != SwWrAccess.NONE: ## non-RO types |
| % if regwen: |
| // from register interface (qualified with register enable) |
| .we (${finst_name}_we & ${regwen}_qs), |
| % else: |
| // from register interface |
| .we (${finst_name}_we), |
| % endif |
| .wd (${finst_name}_wd), |
| % else: ## RO types |
| .we (1'b0), |
| .wd ('0 ), |
| % endif |
| |
| // from internal hardware |
| % if hwaccess == HwAccess.HRO or hwaccess == HwAccess.NONE: |
| .de (1'b0), |
| .d ('0 ), |
| % else: |
| .de (hw2reg.${fsig_name}.de), |
| .d (hw2reg.${fsig_name}.d ), |
| % endif |
| |
| // to internal hardware |
| % if hwaccess == HwAccess.HWO or hwaccess == HwAccess.NONE: |
| .qe (), |
| .q (), |
| % else: |
| % if hwqe: |
| .qe (reg2hw.${fsig_name}.qe), |
| % else: |
| .qe (), |
| % endif |
| .q (reg2hw.${fsig_name}.q ), |
| % endif |
| |
| % if not shadowed: |
| % if swrdaccess != SwRdAccess.NONE: |
| // to register interface (read) |
| .qs (${finst_name}_qs) |
| % else: |
| .qs () |
| % endif |
| % else: |
| % if swrdaccess != SwRdAccess.NONE: |
| // to register interface (read) |
| .qs (${finst_name}_qs), |
| % else: |
| .qs (), |
| % endif |
| |
| // Shadow register error conditions |
| .err_update (reg2hw.${fsig_name}.err_update ), |
| .err_storage (reg2hw.${fsig_name}.err_storage) |
| % endif |
| ); |
| % endif ## end non-constant prim_subreg |
| % endif |
| </%def>\ |
| <%def name="we_gen(sig_name, msb, lsb, swrdaccess, swwraccess, hwext, shadowed, idx)">\ |
| % if swwraccess != SwWrAccess.NONE: |
| % if swrdaccess != SwRdAccess.RC: |
| assign ${sig_name}_we = addr_hit[${idx}] & reg_we & ~wr_err; |
| assign ${sig_name}_wd = reg_wdata[${str_bits_sv(msb,lsb)}]; |
| % else: |
| ## Generate WE based on read request, read should clear |
| assign ${sig_name}_we = addr_hit[${idx}] & reg_re; |
| assign ${sig_name}_wd = '1; |
| % endif |
| % endif |
| % if (swrdaccess != SwRdAccess.NONE and hwext) or shadowed: |
| assign ${sig_name}_re = addr_hit[${idx}] && reg_re; |
| % endif |
| </%def>\ |
| <%def name="rdata_gen(sig_name, msb, lsb, swrdaccess)">\ |
| % if swrdaccess != SwRdAccess.NONE: |
| reg_rdata_next[${str_bits_sv(msb,lsb)}] = ${sig_name}_qs; |
| % else: |
| reg_rdata_next[${str_bits_sv(msb,lsb)}] = '0; |
| % endif |
| </%def>\ |
| <%def name="print_param(params)">\ |
| <% num_params = len(params) %>\ |
| % if num_params != 0: |
| #( |
| % for i,p in enumerate(params): |
| parameter ${p["type"]} ${p["name"]} = ${p["default"]}${"," if i != num_params-1 else ""} |
| % endfor |
| ) \ |
| % endif |
| </%def>\ |