From 70174e7e17ec0d7239b6ad141a17001f6530edcd Mon Sep 17 00:00:00 2001 From: Lorenz Schmid Date: Thu, 19 Oct 2023 16:27:56 +0200 Subject: [PATCH] Add virtual register type A virtual register acts like a small memory element having no input or output port. --- doc/cheby-ug.txt | 3 + proto/cheby/expand_hdl.py | 20 ++++- proto/cheby/hdl/genreg.py | 40 +++++++-- proto/tests.py | 1 + testfiles/features/no_port.cheby | 15 ++++ testfiles/features/no_port.vhdl | 140 +++++++++++++++++++++++++++++++ 6 files changed, 210 insertions(+), 9 deletions(-) create mode 100644 testfiles/features/no_port.cheby create mode 100644 testfiles/features/no_port.vhdl diff --git a/doc/cheby-ug.txt b/doc/cheby-ug.txt index d700114d..b58623a0 100644 --- a/doc/cheby-ug.txt +++ b/doc/cheby-ug.txt @@ -405,6 +405,9 @@ The `type` attribute within `x-hdl` is available. It can be set to: bus. The value of the register is available on the ports. This is the default unless register access type is `ro`. +`no-port`:: A register is created that can be read or written from the bus but +has no input or output ports. It can be used like a small memory. + `wire`:: No logic is created. In case of read from the bus, the current value on the input ports is returned. The output ports are directly connected to the data bus. It doesn't make sense not to also have a strobe port for diff --git a/proto/cheby/expand_hdl.py b/proto/cheby/expand_hdl.py index 1793d60e..be6da74f 100644 --- a/proto/cheby/expand_hdl.py +++ b/proto/cheby/expand_hdl.py @@ -76,9 +76,16 @@ def expand_x_hdl_field_type(n, v): """Decode and check attribute 'type' with value :arg v: :arg n: node for error""" res = parser.read_text(n, 'type', v) - if res not in ['wire', 'reg', 'const', 'autoclear', 'or-clr', 'or-clr-out']: - parser.error("incorrect value for 'type' in x-hdl of {}".format( - n.get_path())) + if res not in [ + "reg", + "no-port", + "wire", + "const", + "autoclear", + "or-clr", + "or-clr-out", + ]: + parser.error("incorrect value for 'type' in x-hdl of {}".format(n.get_path())) return res @@ -94,6 +101,13 @@ def expand_x_hdl_field_kv(f, n, k, v): def expand_x_hdl_field_validate(f): # Validate x-hdl attributes + if f.hdl_type == "no-port": + if f.parent.access != "rw": + parser.error( + "{}: 'no-port' x-hdl.type not allowed for '{}' access".format( + f.get_path(), f.parent.access + ) + ) if f.hdl_type == 'const': if f.parent.access == 'wo': parser.error("{}: 'const' x-hdl.type not allowed for 'wo' access".format( diff --git a/proto/cheby/hdl/genreg.py b/proto/cheby/hdl/genreg.py index da31c021..c45156dd 100644 --- a/proto/cheby/hdl/genreg.py +++ b/proto/cheby/hdl/genreg.py @@ -141,6 +141,32 @@ def assign_reg(self, then_stmts, else_stmts, off, ibus): else: then_stmts.append(HDLAssign(reg, dat)) + +class GenFieldNoPort(GenFieldBase): + """No-port register having no input and output ports""" + def need_reg(self): + return True + + def get_input(self, off): + return self.extract_reg(off, self.field.h_reg) + + def assign_reg(self, then_stmts, else_stmts, off, ibus): + reg, dat, mask = self.extract_reg_dat( + off, self.field.h_reg, ibus.wr_dat, ibus.wr_sel + ) + if self.root.c_wmask_reg and mask is not None: + then_stmts.append( + HDLAssign( + reg, + HDLOr( + HDLParen(HDLAnd(reg, HDLNot(mask))), HDLParen(HDLAnd(dat, mask)) + ), + ) + ) + else: + then_stmts.append(HDLAssign(reg, dat)) + + class GenFieldWire(GenFieldBase): def need_iport(self): return self.reg.access in ['ro', 'rw'] @@ -241,12 +267,14 @@ def connect_output(self, stmts, ibus): class GenReg(ElGen): FIELD_GEN = { - 'reg': GenFieldReg, - 'wire': GenFieldWire, - 'const': GenFieldConst, - 'autoclear': GenFieldAutoclear, - 'or-clr': GenFieldOrClr, - 'or-clr-out': GenFieldOrClrOut} + "reg": GenFieldReg, + "no-port": GenFieldNoPort, + "wire": GenFieldWire, + "const": GenFieldConst, + "autoclear": GenFieldAutoclear, + "or-clr": GenFieldOrClr, + "or-clr-out": GenFieldOrClrOut, + } def field_decode(self, f, off, dat): """Handle multi-word accesses. Slice (if needed) VAL and DAT for offset diff --git a/proto/tests.py b/proto/tests.py index 4c3f353b..dc96cad5 100755 --- a/proto/tests.py +++ b/proto/tests.py @@ -370,6 +370,7 @@ def test_hdl_ref(): 'features/iogroup1', 'features/iogroup2', 'features/repeat-iogroup1', 'features/repeat-iogroup2', 'features/repeat-iogroup3', 'features/repeat-iogroup4', + 'features/no_port', 'issue52/hwInfo', 'bug-gen_wt/m1', 'issue59/inherit', 'issue64/simple_reg1', 'issue66/m1', diff --git a/testfiles/features/no_port.cheby b/testfiles/features/no_port.cheby new file mode 100644 index 00000000..6158bc4a --- /dev/null +++ b/testfiles/features/no_port.cheby @@ -0,0 +1,15 @@ +memory-map: + bus: apb-32 + name: no_port + description: Submap to be used for tests + children: + - reg: + name: reg0 + access: rw + width: 32 + x-hdl: + type: no-port + - reg: + name: reg1 + access: rw + width: 32 diff --git a/testfiles/features/no_port.vhdl b/testfiles/features/no_port.vhdl new file mode 100644 index 00000000..59c20d64 --- /dev/null +++ b/testfiles/features/no_port.vhdl @@ -0,0 +1,140 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity no_port is + port ( + pclk : in std_logic; + presetn : in std_logic; + paddr : in std_logic_vector(2 downto 2); + psel : in std_logic; + pwrite : in std_logic; + penable : in std_logic; + pready : out std_logic; + pwdata : in std_logic_vector(31 downto 0); + pstrb : in std_logic_vector(3 downto 0); + prdata : out std_logic_vector(31 downto 0); + pslverr : out std_logic; + + -- REG reg1 + reg1_o : out std_logic_vector(31 downto 0) + ); +end no_port; + +architecture syn of no_port is + signal wr_req : std_logic; + signal wr_addr : std_logic_vector(2 downto 2); + signal wr_data : std_logic_vector(31 downto 0); + signal rd_req : std_logic; + signal rd_addr : std_logic_vector(2 downto 2); + signal rd_data : std_logic_vector(31 downto 0); + signal wr_ack : std_logic; + signal rd_ack : std_logic; + signal reg0_reg : std_logic_vector(31 downto 0); + signal reg0_wreq : std_logic; + signal reg0_wack : std_logic; + signal reg1_reg : std_logic_vector(31 downto 0); + signal reg1_wreq : std_logic; + signal reg1_wack : std_logic; + signal rd_ack_d0 : std_logic; + signal rd_dat_d0 : std_logic_vector(31 downto 0); + signal wr_req_d0 : std_logic; + signal wr_adr_d0 : std_logic_vector(2 downto 2); + signal wr_dat_d0 : std_logic_vector(31 downto 0); +begin + + -- Write Channel + wr_req <= (psel and pwrite) and not penable; + wr_addr <= paddr; + wr_data <= pwdata; + + -- Read Channel + rd_req <= (psel and not pwrite) and not penable; + rd_addr <= paddr; + prdata <= rd_data; + pready <= wr_ack or rd_ack; + pslverr <= '0'; + + -- pipelining for wr-in+rd-out + process (pclk) begin + if rising_edge(pclk) then + if presetn = '0' then + rd_ack <= '0'; + wr_req_d0 <= '0'; + else + rd_ack <= rd_ack_d0; + rd_data <= rd_dat_d0; + wr_req_d0 <= wr_req; + wr_adr_d0 <= wr_addr; + wr_dat_d0 <= wr_data; + end if; + end if; + end process; + + -- Register reg0 + process (pclk) begin + if rising_edge(pclk) then + if presetn = '0' then + reg0_reg <= "00000000000000000000000000000000"; + reg0_wack <= '0'; + else + if reg0_wreq = '1' then + reg0_reg <= wr_dat_d0; + end if; + reg0_wack <= reg0_wreq; + end if; + end if; + end process; + + -- Register reg1 + reg1_o <= reg1_reg; + process (pclk) begin + if rising_edge(pclk) then + if presetn = '0' then + reg1_reg <= "00000000000000000000000000000000"; + reg1_wack <= '0'; + else + if reg1_wreq = '1' then + reg1_reg <= wr_dat_d0; + end if; + reg1_wack <= reg1_wreq; + end if; + end if; + end process; + + -- Process for write requests. + process (wr_adr_d0, wr_req_d0, reg0_wack, reg1_wack) begin + reg0_wreq <= '0'; + reg1_wreq <= '0'; + case wr_adr_d0(2 downto 2) is + when "0" => + -- Reg reg0 + reg0_wreq <= wr_req_d0; + wr_ack <= reg0_wack; + when "1" => + -- Reg reg1 + reg1_wreq <= wr_req_d0; + wr_ack <= reg1_wack; + when others => + wr_ack <= wr_req_d0; + end case; + end process; + + -- Process for read requests. + process (rd_addr, rd_req, reg0_reg, reg1_reg) begin + -- By default ack read requests + rd_dat_d0 <= (others => 'X'); + case rd_addr(2 downto 2) is + when "0" => + -- Reg reg0 + rd_ack_d0 <= rd_req; + rd_dat_d0 <= reg0_reg; + when "1" => + -- Reg reg1 + rd_ack_d0 <= rd_req; + rd_dat_d0 <= reg1_reg; + when others => + rd_ack_d0 <= rd_req; + end case; + end process; +end syn;