Close

Implementation in VHDL - RAM and lookup tables

A project log for Iskra EMZ1001A - a virtual resurrection

4-bits wonder to print "Hello World!" and to calculate Fibonacci numbers to 13 decimal digits!

zpekiczpekic 12/27/2022 at 22:000 Comments

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:

-- RAM is pointed by BU and BL
ram_addr <= mr_bu & mr_bl;
ram <= mr_ram(to_integer(unsigned(ram_addr)));

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:

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