To learn VHDL, I'm implementing my own custom CPU with VHDL.
Tired of writing opcode bit pattern manually, I want to create very simple "assembler" to create bit pattern.
Here is current implemenation of such assembler:
library ieee;
use ieee.std_logic_1164.all;
use work.utility.all;
package assembler is
function encode_opcode(cmd: string; re: integer; rd: integer; ra: integer; rb: integer; imm: integer) return std_logic_vector;
end;
package body assembler is
function to_lower(x: character) return character is
constant posA: integer := character'pos('A');
constant posZ: integer := character'pos('Z');
constant posLowerA: integer := character'pos('a');
constant posX: integer := character'pos(x);
begin
if posA <= posX and posX <= posZ then
return character'val(posX - posA + posLowerA);
else
return x;
end if;
end;
function to_lower(x: string) return string is
variable r: string(x'range);
begin
for i in x'range loop
r(i) := to_lower(x(i));
end loop;
return r;
end;
function encode_cmd(cmd: string) return std_logic_vector is
constant cmd2: string(1 to cmd'length) := cmd;
variable cmd3: string(1 to 5) := "-----";
begin
if cmd'length > 5 then
report "Illegal command: " & cmd severity error;
return "";
end if;
for i in cmd2'range loop
cmd3(i) := cmd2(i);
end loop;
case to_lower(cmd3) is
-- Group 1: Memory access (omitted)
-- Group 2: Addition
when "addu-" => return "00100000";
when "subu-" => return "00100001";
when "add--" => return "00100010";
when "sub--" => return "00100011";
-- Group 3: Multiplication
when "multu" => return "00110000";
when "divu-" => return "00110001";
when "mult-" => return "00110010";
when "div--" => return "00110011";
-- Group 4: Bitwise (omitted)
-- Group 5: Shift
when "shf--" => return "01010000";
when "shfu-" => return "01010001";
when "rot--" => return "01010010";
-- Group 6: Branch absolute (omitted)
-- Group 7: Branch relative (omitted)
when others =>
report "Illegal command: " & cmd severity error;
return "";
end case;
end;
function encode_opcode(cmd: string; re: integer; rd: integer; ra: integer; rb: integer; imm: integer) return std_logic_vector is
begin
return encode_cmd(cmd) & encode_unsigned(re, 5) & encode_unsigned(rd, 5) & encode_unsigned(ra, 5) & encode_unsigned(rb, 5) & encode_signed(imm, 4);
end;
end;
Here is definition of encode_unsigned and encode_signed function:
function encode_unsigned(x: integer; n: integer) return std_logic_vector is
begin
return std_logic_vector(to_unsigned(x, n));
end;
function encode_signed(x: integer; n: integer) return std_logic_vector is
begin
return std_logic_vector(to_signed(x, n));
end;
Here is usage of encode_opcode function:
function rom_contents(addr: std_logic_vector) return std_logic_vector is
begin
case decode_unsigned(addr) is
when 0 => return encode_opcode("ADDu", 0, 1, 0, 0, 2);
when 1 => return encode_opcode("ADD", 0, 2, 0, 0, -8);
when 2 => return encode_signed(11, 32);
when 3 => return encode_opcode("MULT", 0, 3, 1, 2, 0);
when 6 => return encode_opcode("ADD", 0, 4, 3, 0, 0);
when 8 => return encode_opcode("ADD", 0, 16, 0, 0, -8);
when 9 => return encode_signed(16#12345678#, 32);
when 11 => return encode_opcode("ROT", 31, 30, 16, 0, 4);
when 12 => return encode_opcode("ROT", 29, 28, 30, 0, 4);
when others => return (31 downto 0 => '0');
end case;
end;
Whenever I make mistake and wrote something like encode_opcode("ROTE", 31, 30, 16, 0, 4);
, I want to get error message like Illegal command: ROTE
. However, it doesn't create any error message, and the length of return value of encode_opcode becomes 24 silently. (if there were no mistakes, lenght should be 32.)
Using enum for command would be too hard, as some of command looks like AND
, XOR
, GFu>
, GT<=
.
I'm using Quartus Prime Lite Edition version 18.0
UPDATE: usage of rom_contents:
entity instruction_memory_controller is
port (
clock: in std_logic;
addr: in std_logic_vector(31 downto 0);
q: out std_logic_vector(31 downto 0)
);
end;
architecture RTL of instruction_memory_controller is
begin
process(clock)
begin
if rising_edge(clock) then
q <= rom_contents(addr);
end if;
end process;
end;
When I entered wrong command, q <= rom_contnts(addr)
marked error due to mismatch size (24 vs 32).
When I changed return value of illegal command from ""
to "XXXXXXXX"
, it compiles without any error/warning. (My expection is that it triggers Illegal command
error by report statement.)