CUGA-1
From Hamsterworks Wiki!
This will be a complete hand rolled processor.
This FPGA Project was started June 2011.
Contents |
Background
I always wanted to build my own CPU, and here's my chance!
Features I want to support - Interrupts - Memory access enforcement - Virtual memory - Virtualisation
Project status
Project started, with initial design started.
ALU Data paths
The processor is based around three buses - the A (blue), B (green) and C (red) buses. Because this is an FPGA project these buses are implemented using multiplexers - internally FPGAs don't have tri-state logic, and having the tools fake just doesn't seem right. There are three places where three input multiplexers are required, when implemented this might create one "slow" path and two "fast" paths (the fast path having to pass through two multiplexers.
In this sketch the above doesn't show most of the control signals. But I'm making this up as I go along!
As of 31Jul2011 top lecwl object for the ALU is:
entity integer_core is
Port ( clk : in STD_LOGIC;
-- ALU control
alu_mode : in std_logic_vector(2 downto 0);
alu_thru_b : in std_logic;
carry_in : in std_logic;
carry_8 : out std_logic;
carry_16 : out std_logic;
carry_32 : out std_logic;
zero_8 : out std_logic;
zero_16 : out std_logic;
zero_32 : out std_logic;
-- Address register control
addr_out_alu : in STD_LOGIC;
addr_out_pc : in STD_LOGIC;
constant_sel : in STD_LOGIC_VECTOR(2 downto 0);
-- Bus routing
bus_a_datain : in STD_LOGIC;
bus_a_const : in STD_LOGIC;
bus_b_pc : in STD_LOGIC;
bus_b_flags : in STD_LOGIC;
-- Program counter control
pc_write : in STD_LOGIC;
pc_write_alu_out : in STD_LOGIC;
pc_offset : in STD_LOGIC_VECTOR (2 downto 0);
-- Register select/control
regSelA : in STD_LOGIC_VECTOR (3 downto 0);
regSelB : in STD_LOGIC_VECTOR (3 downto 0);
commit : in STD_LOGIC;
rollback : in STD_LOGIC;
regWrite : in STD_LOGIC;
-- major buses
data_in : in STD_LOGIC_VECTOR (31 downto 0);
data_out : out STD_LOGIC_VECTOR (31 downto 0);
address : out STD_LOGIC_VECTOR (31 downto 0)
);
end integer_core;
Register set
Due to the internals of an FPGA, the register set will be based around a set of 16 registers (matching the performance of the FPGA's 4x4 lookup tables. Because the register set will be implemented as dual port RAM, the contents of two registers will available at any time, but only one register will be writable.
Because I want to support virtual memory I need some way of rolling back partially complete transactions. My plan is to use two register banks, and a bit vector to keep track of which one is current and directing the writes into the other bank.
When a instruction commits the bit vector will be updated by XORing a mask based on the updated registers. Should an instruction need to be rolled out (e.g. due to page fault or instruction trap) the bit vector will not be updated, preserving the register's original contents. In general, the registers will be committed while the next instruction is being fetched.
The register bank will supply values to the A and B buses, and accept writes from the C bus.
Word size
I don't have any firm view on word size at the moment, other than the address size will be the word size, and the word size will be 2^n*8 bits. So the choices are 16, 32 or 64, with 64 bits being too large for implementation in a small FPGA. 32 bits is the way to go, and maybe drop down to 16 bits if it proves to be too big.
Constant store
A dedicated read-only store will also be able to supply a set of constants to the B Bus (e.g 0, +1, -1 or others such as offsets within the IRQ frame).
| Value | Reason |
| 0 | For do-nothings or when adding just the carry flag |
| 1 | Increment, updating the stack pointer |
| -1 | Decrements, and updating the stack pointer |
| 2 | Size of a an interrupt stack frame |
| -2 | Size of a an interrupt stack frame |
| 4,8,16... | Bit masks for bit operations |
ALU
The ALU will take values from the A and B buses and compute a result to place on the C bus. It will also update the relevant processor status flags.
ALU operations
| Mode bits | Operation | Notes |
|---|---|---|
| 000 | C = A | alu_thru_b = '0' |
| 000 | C = B | alu_thru_b = '1' |
| 001 | C = A and B | |
| 001 | C = A or B | |
| 001 | C = A xor B | |
| 001 | C = A + B | |
| 001 | C = A + B + carry | Add with carry |
| 001 | C = A + not B + 1 | Subtract |
| 001 | C = A + not B + carry | Subtract with borrow |
Memory interface
Memory Address Register
The memory interface will consist of the Address register, feed from either the A or C bus, or from the Program counter with an offset added. Because the Address register can be source from either side of the ALU operations like PUSH and POP can be optimised.
The Memory Address register is overwritten with any memory operation the value can't be assumed to persist outside of the current instruction.
DataIn Register
When a memory read completes, the result is stored in the DataIn register, which can be placed onto the A bus. Logic before this register deals with memory alignment issues.
As the value in the Data In register is overwritten with any memory read the value can't be assumed to persist outside of the current instruction.
DataOut Register
Data to be written to memory is placed into the data out register. Along with the value, the execution unit will supply the size of the write area to the MMU, allowing for a trap to be generated in anticipation of a future write for this instruction causing a trap (e.g. running out of stack when an IRQ occurs).
As the data value in the Data Out register can't be retrieved, the value can't be assumed to persist outside of the current instruction.
Definition for the integer core
This is the definition of the integer core. I've got it here for reference.
entity integer_core is
Port ( clk : in STD_LOGIC;
-- ALU control
alu_mode : in std_logic_vector(2 downto 0);
carry_in : in std_logic;
carry_8 : out std_logic;
carry_16 : out std_logic;
carry_32 : out std_logic;
zero_8 : out std_logic;
zero_16 : out std_logic;
zero_32 : out std_logic;
-- Address register control
addr_out_alu : in STD_LOGIC;
addr_out_pc : in STD_LOGIC;
constant_sel : in STD_LOGIC_VECTOR(2 downto 0);
-- Bus routing
bus_a_datain : in STD_LOGIC;
bus_a_const : in STD_LOGIC;
bus_b_pc : in STD_LOGIC;
bus_b_flags : in STD_LOGIC;
-- Program counter control
pc_write : in STD_LOGIC;
pc_write_alu_out : in STD_LOGIC;
pc_offset : in STD_LOGIC_VECTOR (2 downto 0);
-- Register select/control
regSelA : in STD_LOGIC_VECTOR (3 downto 0);
regSelB : in STD_LOGIC_VECTOR (3 downto 0);
commit : in STD_LOGIC;
rollback : in STD_LOGIC;
regWrite : in STD_LOGIC;
-- major buses
data_in : in STD_LOGIC_VECTOR (31 downto 0);
data_out : out STD_LOGIC_VECTOR (31 downto 0);
address : out STD_LOGIC_VECTOR (31 downto 0)
);
end integer_core;
