Skip to content

Commit

Permalink
Add virtual register type
Browse files Browse the repository at this point in the history
A virtual register acts like a small memory element having no input or
output port.
  • Loading branch information
lorenzschmid committed Oct 20, 2023
1 parent 5de7449 commit 70174e7
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 9 deletions.
3 changes: 3 additions & 0 deletions doc/cheby-ug.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 17 additions & 3 deletions proto/cheby/expand_hdl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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(
Expand Down
40 changes: 34 additions & 6 deletions proto/cheby/hdl/genreg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions proto/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
15 changes: 15 additions & 0 deletions testfiles/features/no_port.cheby
Original file line number Diff line number Diff line change
@@ -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
140 changes: 140 additions & 0 deletions testfiles/features/no_port.vhdl
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 70174e7

Please sign in to comment.