RAM
All EMZ1001A RAM is implemented on chip, as 64 nibbles, arranged as 4 * 16 matrix, addressed by 2 bit BU (mr_bu) and 4-bit BL (mr_bl) registers. There is really no provision for external RAM, although possibly it could be implemented by INP and OUT instructions combined with manipulation of A lines, but it would be very inefficient.
RAM is implemented as 2-port read, 1 port write:
- System read is always RAM[BU, BL]
-- RAM is pointed by BU and BL
ram_addr <= mr_bu & mr_bl;
ram <= mr_ram(to_integer(unsigned(ram_addr)));
- Debug read is always RAM[dbg_sel]
-- select debug outputs (RAM) dbg_mem <= mr_ram(to_integer(unsigned(dbg_sel)));
dbg_sel is generated by VGA going over rows and columns of the screen, so each RAM cell ends up displayed on their position in the 16*16 debug window
Write operations are driven in the main on_clk_down() process, so the input MUX is generated inside the state machine. The most interesting operations are set and reset single bit of memory:
when opr_stm => -- STM
mr_ram(to_integer(unsigned(ram_addr))) <= ram or mask8(7 downto 4);
when opr_rsm => -- RSM
mr_ram(to_integer(unsigned(ram_addr))) <= ram and mask8(3 downto 0);
No ALU is used for these. Because output of RAM is always known (RAM[BU, BL]), and bit position can only take 4 values, a lookup table implements all the valid AND and OR combinations which are then simply fed to RAM MUX input:
-- mask for STM (upper nibble) and RSM (lower nibble)
with ir_current(1 downto 0) select mask8 <=
"00011110" when "00",
"00101101" when "01",
"01001011" when "10",
"10000111" when others;
Lookup tables
FPGAs has lots of RAM type resources - both inside slices where they can be used to implement logic, or RAM/ROM, or dedicated RAM blocks for bigger size memories. Lots of random logic can be saved by driving the design through lookup tables. Good example is BL (mr_bl) 4-bit register:
- Value must be checked for 15, 0, 12
- Value must be incremented / decremented
- Value must be used to decode one out of 13 A lines (either as high or low)
All of these are accomplished using single lookup table which has 16 entries and the address is directly BL register:
-- used for PSH (OR, non-inverted) and PSL (AND, inverted)
constant psx_mask: mem16x40 := (
X"1F" & "11111111111111100000000000000001",
X"20" & "11111111111111010000000000000010",
X"31" & "11111111111110110000000000000100",
X"42" & "11111111111101110000000000001000",
X"53" & "11111111111011110000000000010000",
X"64" & "11111111110111110000000000100000",
X"75" & "11111111101111110000000001000000",
X"86" & "11111111011111110000000010000000",
X"97" & "11111110111111110000000100000000",
X"A8" & "11111101111111110000001000000000",
X"B9" & "11111011111111110000010000000000",
X"CA" & "11110111111111110000100000000000",
X"DB" & "11101111111111110001000000000000",
X"EC" & "11011111111111110010000000000000",
X"FD" & "10111111111111110100000000000000",
X"0E" & "00000000000000001111111111111111" -- set (or clear) all bits
);
Upper 8-bits are increment and decrement values, which saves 2 adders, or 1 adder with logic generating 0001 or 1111 on one input saved.
signal psx: std_logic_vector(39 downto 0);
alias psx_ormask: std_logic_vector(15 downto 0) is psx(15 downto 0);
alias psx_andmask: std_logic_vector(15 downto 0) is psx(31 downto 16);
alias bl_dec: std_logic_vector(3 downto 0) is psx(35 downto 32);
alias bl_inc: std_logic_vector(3 downto 0) is psx(39 downto 36);
alias bl_is_0: std_logic is psx(0);
alias bl_is_13: std_logic is psx(13);
alias bl_is_15: std_logic is psx(15);
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.