[otbn] Add separate FG0 & FG1 CSRs Fixes #3693 Signed-off-by: Greg Chadwick <gac@lowrisc.org>
diff --git a/hw/ip/otbn/doc/_index.md b/hw/ip/otbn/doc/_index.md index 595eafb..10205c5 100644 --- a/hw/ip/otbn/doc/_index.md +++ b/hw/ip/otbn/doc/_index.md
@@ -124,9 +124,52 @@ <td>0x7C0</td> <td>RW</td> <td> + <strong>FG0</strong>. + Wide arithmetic flag group 0. + This CSR provides access to flag group 0 used by wide integer arithmetic. + <strong>FLAGS</strong>, <strong>FG0</strong> and <strong>FG1</strong> provide different views on the same underlying bits. + <table> + <thead> + <tr><th>Bit</th><th>Description</th></tr> + </thead> + <tbody> + <tr><td>0</td><td>Carry of Flag Group 0</td></tr> + <tr><td>1</td><td>MSb of Flag Group 0</td></tr> + <tr><td>2</td><td>LSb of Flag Group 0</td></tr> + <tr><td>3</td><td>Zero of Flag Group 0</td></tr> + </tbody> + </table> + </td> + </tr> + <tr> + <td>0x7C1</td> + <td>RW</td> + <td> + <strong>FG1</strong>. + Wide arithmetic flag group 1. + This CSR provides access to flag group 1 used by wide integer arithmetic. + <strong>FLAGS</strong>, <strong>FG0</strong> and <strong>FG1</strong> provide different views on the same underlying bits. + <table> + <thead> + <tr><th>Bit</th><th>Description</th></tr> + </thead> + <tbody> + <tr><td>0</td><td>Carry of Flag Group 1</td></tr> + <tr><td>1</td><td>MSb of Flag Group 1</td></tr> + <tr><td>2</td><td>LSb of Flag Group 1</td></tr> + <tr><td>3</td><td>Zero of Flag Group 1</td></tr> + </tbody> + </table> + </td> + </tr> + <tr> + <td>0x7C8</td> + <td>RW</td> + <td> <strong>FLAGS</strong>. - Wide arithmetic flags. - This CSR provides access to the flags used in wide integer arithmetic. + Wide arithmetic flag groups. + This CSR provides access to both flags groups used by wide integer arithmetic. + <strong>FLAGS</strong>, <strong>FG0</strong> and <strong>FG1</strong> provide different views on the same underlying bits. <table> <thead> <tr><th>Bit</th><th>Description</th></tr>
diff --git a/hw/ip/otbn/dv/otbnsim/sim/csr.py b/hw/ip/otbn/dv/otbnsim/sim/csr.py index 4efaa6c..9ab87ad 100644 --- a/hw/ip/otbn/dv/otbnsim/sim/csr.py +++ b/hw/ip/otbn/dv/otbnsim/sim/csr.py
@@ -2,10 +2,6 @@ # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -from typing import List - -from riscvmodel.types import Trace # type: ignore - from .flags import FlagGroups from .wsr import WSRFile @@ -15,17 +11,32 @@ def __init__(self) -> None: self.flags = FlagGroups() + @staticmethod + def _get_field(field_idx: int, field_size: int, val: int) -> int: + mask = (1 << field_size) - 1 + return (val >> (field_size * field_idx)) & mask + + @staticmethod + def _set_field(field_idx: int, field_size: int, field_val: int, + old_val: int) -> int: + mask = (1 << field_size) - 1 + shift = field_size * field_idx + return (old_val & ~(mask << shift)) | (field_val << shift) + def read_unsigned(self, wsrs: WSRFile, idx: int) -> int: - if idx == 0x7c0: + if 0x7c0 <= idx <= 0x7c1: + # FG0/FG1 + fg = idx - 0x7c0 + return self._get_field(fg, 4, self.flags.read_unsigned()) + + if idx == 0x7c8: # FLAGS register return self.flags.read_unsigned() if 0x7d0 <= idx <= 0x7d7: # MOD0 .. MOD7. MODi is bits [32*(i+1)-1..32*i] - i = idx - 0x7d0 - mod_val = wsrs.MOD.read_unsigned() - mask32 = (1 << 32) - 1 - return (mod_val >> (32 * i)) & mask32 + mod_n = idx - 0x7d0 + return self._get_field(mod_n, 32, wsrs.MOD.read_unsigned()) if idx == 0xfc0: # RND register @@ -36,19 +47,23 @@ def write_unsigned(self, wsrs: WSRFile, idx: int, value: int) -> None: assert 0 <= value < (1 << 32) - if idx == 0x7c0: + if 0x7c0 <= idx <= 0x7c1: + # FG0/FG1 + fg = idx - 0x7c0 + old = self.flags.read_unsigned() + self.flags.write_unsigned(self._set_field(fg, 4, value, old)) + return + + if idx == 0x7c8: # FLAGS register self.flags.write_unsigned(value) return if 0x7d0 <= idx <= 0x7d7: # MOD0 .. MOD7. MODi is bits [32*(i+1)-1..32*i]. read,modify,write. - i = idx - 0x7d0 - old_val = wsrs.MOD.read_unsigned() - shifted_mask = ((1 << 32) - 1) << (32 * i) - cleared = old_val & ~shifted_mask - new_val = cleared | (value << (32 * i)) - wsrs.MOD.write_unsigned(new_val) + mod_n = idx - 0x7d0 + old = wsrs.MOD.read_unsigned() + wsrs.MOD.write_unsigned(self._set_field(mod_n, 32, value, old)) return if idx == 0xfc0:
diff --git a/hw/ip/otbn/dv/smoke/smoke_test.s b/hw/ip/otbn/dv/smoke/smoke_test.s index 94bd991..f984d6f 100644 --- a/hw/ip/otbn/dv/smoke/smoke_test.s +++ b/hw/ip/otbn/dv/smoke/smoke_test.s
@@ -182,7 +182,7 @@ bn.subb w17, w3, w4, FG1 # x24 = {fg1, fg0} = 0x55 -csrrs x24, 0x7c0, x0 +csrrs x24, 0x7c8, x0 # w18 = w1 + (w2 << 17B) = 0x1296659f_bbc28370_23634ee9_22168a4e_c79af825_69be586e_9866bb3b_53769ada bn.add w18, w1, w2 << 17B
diff --git a/hw/ip/otbn/rtl/otbn_controller.sv b/hw/ip/otbn/rtl/otbn_controller.sv index 51a029c..e93e3cd 100644 --- a/hw/ip/otbn/rtl/otbn_controller.sv +++ b/hw/ip/otbn/rtl/otbn_controller.sv
@@ -112,8 +112,10 @@ logic [ImemAddrWidth-1:0] next_insn_addr; csr_e csr_addr; + logic [31:0] csr_rdata_raw; logic [31:0] csr_rdata; logic [BaseWordsPerWLEN-1:0] csr_rdata_mux [32]; + logic [31:0] csr_wdata_raw; logic [31:0] csr_wdata; wsr_e wsr_addr; @@ -459,7 +461,7 @@ ispr_word_addr_base = '0; unique case (csr_addr) - CsrFlags: begin + CsrFlags, CsrFg0, CsrFg1 : begin ispr_addr_base = IsprFlags; ispr_word_addr_base = '0; end @@ -485,10 +487,34 @@ assign csr_rdata_mux[i_bit][i_word] = ispr_rdata_i[i_word*32 + i_bit] & ispr_word_sel_base[i_word]; end - assign csr_rdata[i_bit] = |csr_rdata_mux[i_bit]; + assign csr_rdata_raw[i_bit] = |csr_rdata_mux[i_bit]; end - assign csr_wdata = insn_dec_shared_i.ispr_rs_insn ? csr_rdata | rf_base_rd_data_a_i : rf_base_rd_data_a_i; + // Specialised read data handling for CSR reads where raw read data needs modification. + always_comb begin + csr_rdata = csr_rdata_raw; + + unique case(csr_addr) + // For FG0/FG1 select out appropriate bits from FLAGS ISPR and pad the rest with zeros. + CsrFg0: csr_rdata = {28'b0, csr_rdata_raw[3:0]}; + CsrFg1: csr_rdata = {28'b0, csr_rdata_raw[7:4]}; + default: ; + endcase + end + + assign csr_wdata_raw = insn_dec_shared_i.ispr_rs_insn ? csr_rdata | rf_base_rd_data_a_i : rf_base_rd_data_a_i; + + // Specialised write data handling for CSR writes where raw write data needs modification. + always_comb begin + csr_wdata = csr_wdata_raw; + + unique case(csr_addr) + // For FG0/FG1 only modify relevant part of FLAGS ISPR. + CsrFg0: csr_wdata = {24'b0, csr_rdata_raw[7:4], csr_wdata_raw[3:0]}; + CsrFg1: csr_wdata = {24'b0, csr_wdata_raw[3:0], csr_rdata_raw[3:0]}; + default: ; + endcase + end assign wsr_addr = wsr_e'(insn_dec_bignum_i.i[WsrNumWidth-1:0]);
diff --git a/hw/ip/otbn/rtl/otbn_pkg.sv b/hw/ip/otbn/rtl/otbn_pkg.sv index 83fecc6..cb9713d 100644 --- a/hw/ip/otbn/rtl/otbn_pkg.sv +++ b/hw/ip/otbn/rtl/otbn_pkg.sv
@@ -160,7 +160,9 @@ // Control and Status Registers (CSRs) parameter int CsrNumWidth = 12; typedef enum logic [CsrNumWidth-1:0] { - CsrFlags = 12'h7C0, + CsrFg0 = 12'h7C0, + CsrFg1 = 12'h7C1, + CsrFlags = 12'h7C8, CsrMod0 = 12'h7D0, CsrMod1 = 12'h7D1, CsrMod2 = 12'h7D2,