URL
https://opencores.org/ocsvn/tinycpu/tinycpu/trunk
Subversion Repositories tinycpu
Compare Revisions
- This comparison shows the changes necessary to convert path
/tinycpu
- from Rev 18 to Rev 19
- ↔ Reverse comparison
Rev 18 → Rev 19
/trunk/testbench/core_tb.vhd
--- trunk/src/registerfile.vhd (revision 18)
+++ trunk/src/registerfile.vhd (revision 19)
@@ -9,32 +9,30 @@
use work.tinycpu.all;
entity registerfile is
-
- port(
- WriteEnable: in regwritetype;
- DataIn: in regdatatype;
- Clock: in std_logic;
- DataOut: out regdatatype
- );
+
+port(
+ WriteEnable: in regwritetype;
+ DataIn: in regdatatype;
+ Clock: in std_logic;
+ DataOut: out regdatatype
+);
end registerfile;
architecture Behavioral of registerfile is
-
type registerstype is array(0 to 15) of std_logic_vector(7 downto 0);
signal registers: registerstype;
--attribute ram_style : string;
--attribute ram_style of registers: signal is "distributed";
begin
- regs:
- for I in 0 to 15 generate
+ regs: for I in 0 to 15 generate
process(WriteEnable(I), DataIn(I), Clock)
- begin
- if rising_edge(Clock) then
- if(WriteEnable(I) = '1') then
- registers(I) <= DataIn(I);
- end if;
- end if;
- end process;
- DataOut(I) <= registers(I);
+ begin
+ if rising_edge(Clock) then
+ if(WriteEnable(I) = '1') then
+ registers(I) <= DataIn(I);
+ end if;
+ end if;
+ end process;
+ DataOut(I) <= registers(I);
end generate regs;
end Behavioral;
\ No newline at end of file
/trunk/src/memory.vhd
71,6 → 71,7
end if; |
end if; |
else |
datawrite <= x"0000"; |
we <= "00"; |
end if; |
end process; |
/trunk/src/core.vhd
0,0 → 1,212
--Core module. |
--This module is basically connects everything and decodes the opcodes. |
--The only thing above this is toplevel.vhd which actually sets the pinout for the FPGA |
|
|
library IEEE; |
use IEEE.STD_LOGIC_1164.ALL; |
use IEEE.NUMERIC_STD.ALL; |
use work.tinycpu.all; |
|
entity core is |
port( |
--memory interface |
MemAddr: out std_logic_vector(15 downto 0); --memory address (in bytes) |
MemWW: out std_logic; --memory writeword |
MemWE: out std_logic; --memory writeenable |
MemOut: in std_logic_vector(15 downto 0); |
MemIn: out std_logic_vector(15 downto 0); |
--general interface |
Clock: in std_logic; |
Reset: in std_logic; --When this is high, CPU will reset within 1 clock cycles. |
--Enable: in std_logic; --When this is high, the CPU executes as normal, when low the CPU stops at the next clock cycle(maintaining all state) |
Hold: in std_logic; --when high, CPU pauses execution and places Memory interfaces into high impendance state so the memory can be used by other components |
HoldAck: out std_logic; --when high, CPU acknowledged hold and buses are in high Z |
--todo: port interface |
|
--debug ports: |
DebugIR: out std_logic_vector(15 downto 0); --current instruction |
DebugIP: out std_logic_vector(15 downto 0); --current IP |
DebugCS: out std_logic_vector(15 downto 0); --current code segment |
DebugTR: out std_logic; --current value of TR |
); |
end core; |
|
architecture Behavioral of core is |
component fetch is |
port( |
Enable: in std_logic; |
AddressIn: in std_logic_vector(15 downto 0); |
Clock: in std_logic; |
DataIn: in std_logic_vector(15 downto 0); --interface from memory |
IROut: out std_logic_vector(15 downto 0); |
AddressOut: out std_logic_vector(15 downto 0) --interface to memory |
); |
end component; |
component alu is |
port( |
Op: in std_logic_vector(4 downto 0); |
DataIn1: in std_logic_vector(7 downto 0); |
DataIn2: in std_logic_vector(7 downto 0); |
DataOut: out std_logic_vector(7 downto 0); |
TR: out std_logic |
); |
end component; |
component carryover is |
port( |
EnableCarry: in std_logic; --When disabled, SegmentIn goes to SegmentOut |
DataIn: in std_logic_vector(7 downto 0); |
SegmentIn: in std_logic_vector(7 downto 0); |
Addend: in std_logic_vector(7 downto 0); --How much to increase DataIn by (as a signed number). Believe it or not, that's the actual word for what we need. |
DataOut: out std_logic_vector(7 downto 0); |
SegmentOut: out std_logic_vector(7 downto 0) |
); |
end component; |
component registerfile is |
port( |
WriteEnable: in regwritetype; |
DataIn: in regdatatype; |
Clock: in std_logic; |
DataOut: out regdatatype |
); |
end component; |
|
constant REGIP: integer := 7; |
constant REGSP: integer := 6; |
constant REGSS: integer := 15; |
constant REGES: integer := 14; |
constant REGDS: integer := 13; |
constant REGCS: integer := 12; |
|
type ProcessorState is ( |
ResetProcessor, |
FirstFetch, |
Execute, |
WaitForMemory, |
HoldMemory |
); |
signal state: ProcessState; |
signal HeldState: ProcessState; --state the processor was in when HOLD was activated |
|
--carryout signals |
signal CarryCS: std_logic; |
signal CarrySS: std_logic; |
signal IPAddend: std_logic_vector(7 downto 0); |
signal SPAddend: std_logic_vector(7 downto 0); |
signal IPCarryOut: std_logic_vector(7 downto 0); |
signal CSCarryOut: std_logic_vector(7 downto 0); |
--register signals |
signal regWE:regwritetype; |
signal regIn: regdatatype; |
signal regOut: regdatatype; |
--fetch signals |
signal fetchEN: std_logic; |
signal IR: std_logic_vector(15 downto 0); |
|
--control signals |
signal InReset: std_logic; |
|
--opcode shortcut signals |
signal opmain: std_logic_vector(3 downto 0); |
signal opimmd: std_logic_vector(7 downto 0); |
signal opcond1: std_logic; --first conditional bit |
signal opcond2: std_logic; --second conditional bit |
signal opreg1: std_logic_vector(2 downto 0); |
signal opreg2: std_logic_vector(2 downto 0); |
signal opreg3: std_logic_vector(2 downto 0); |
signal opseges: std_logic; --use ES segment |
|
begin |
reg: port map registerfile( |
WriteEnable => regWE, |
DataIn => regIn, |
Clock => Clock, |
DataOut => regOut |
); |
carryovercs: port map carryover( |
EnableCarry => CarryCS, |
DataIn => regOut(REGIP); |
SegmentIn => regOut(REGCS); |
Addend => IPAddend; |
DataOut => IPCarryOut; |
SegmentOut => CSCarryOut;\ |
); |
fetcher: port map fetch( |
Enable => fetchEN, |
AddressIn => regOut(REGCS) & regOut(REGIP), |
Clock => Clock, |
DataIn => MemIn, |
IROut => IR, |
AddressOut => MemAddr --this component supports tristate, so no worries about an intermediate signal |
); |
|
opmain <= IR(15 downto 12); |
opimmd <= IR(7 downto 0); |
opcond1 <= IR(8); |
opcond2 <= IR(7); |
opreg1 <= IR(11 downto 9); |
opreg3 <= IR(2 downto 0); |
opreg2 <= IR(5 downto 3); |
opseges <= IR(6); |
|
states: process() |
begin |
if rising_edge(Clock) then |
if reset='1' then |
InReset <= '1'; |
state <= ResetProcessor; |
CarryCS <= '1'; |
CarrySS <= '0'; |
--finish up |
elsif InReset='1' and reset='0' and Hold='0' then --reset is done, start executing |
InReset <= '0'; |
state <= FirstFetch; |
fetchEN <= '1'; |
elsif Hold = '1' and (state=HoldMemory or state=Execute or state=ResetProcessor) then |
state <= HoldMemory; |
HoldAck <= '1'; |
FetchEN <= '0'; |
MemAddr <= "ZZZZZZZZZZZZZZZZ"; |
MemIn <= "ZZZZZZZZZZZZZZZZ"; |
elsif Hold='0' and state=HoldMemory then |
state <= ResetProcessor when reset='1' else Execute; |
FetchEN <= '1'; |
elsif state=FirstFetch then --we have to let IR get loaded before we can execute. |
state <= Execute; |
end if; |
|
end if; |
end process; |
|
decode: process() |
begin |
if rising_edge(Clock) and Hold='0' then |
if state=Execute then |
--reset to "usual" |
RegIn(REGIP) <= IPCarryOut; |
RegIn(REGCS) <= CSCarryOut; |
RegWE <= (others => '0'); |
|
--actual decoding |
case opmain is |
when "0000" => --mov reg,imm |
RegIn(to_integer(unsigned(opreg1))) <= opimmd; |
RegWE(to_integer(unsigned(opreg1))) <= '1'; |
when others => |
--synthesis off |
report "Not implemented" severity error; |
--synthesis on |
end case; |
end if; |
end if; |
end process; |
|
|
|
|
|
|
|
|
|
end Behavioral; |
/trunk/docs/design.md.txt
45,10 → 45,19
1. move reg, immediate |
2. move [reg], immediate |
3. push and move reg, immediate (or call immediate) |
4. push immediate |
5. move (relative) reg, immediate |
4. move (relative) reg, immediate |
|
mini-group 5. Root opcode is 5, register is to tell which opcode( up to 8). No register room, only immediate |
push immedate |
XX |
XX |
XX |
XX |
XX |
XX |
XX |
|
|
groups: (limited to 2 registers and no immediates. each group has 8 opcodes) |
group 1: |
move(store) [reg],reg |
55,8 → 64,8
move(load) reg,[reg] |
out reg1,reg2 (output to port reg1 value reg2) |
in reg1,reg2 (input from port reg2 and store in reg1) |
pop reg |
push reg |
XX |
XX |
move segmentreg,reg |
move reg,segmentreg |
|
86,12 → 95,12
push and move reg, reg (or call reg) |
exchange reg,reg |
exchange reg,seg |
clear TR |
Set TR |
XX |
XX |
|
group 5: |
increment reg |
decrement reg |
XX |
XX |
far jmp reg1, reg2 (CS=reg1 and IP=reg2) |
far call reg1,reg2 |
far jmp [reg] (first byte is CS, second byte is IP) |
109,54 → 118,46
mov relative reg, reg |
exchange reg, reg |
|
super group: Super groups only have room for 1 register argument. Each subgroup has 8 opcodes, capable of 8 subgroups. |
subgroup 0: |
push reg |
pop reg |
set TR |
reset TR |
increment reg |
decrement reg |
set register bank 0 |
set register bank 1 |
subgroup 1: |
enable carryover seg |
disable carryover seg |
|
|
|
3 register instructions: |
1. add reg1, reg2, reg3 (reg1=reg2+reg3) |
2. sub reg1, reg2, reg3 |
|
|
opcodes used: 13 of 16. 3 more opcodes available. Decide what to do with the room later. |
opcodes used: 14 of 16. 2 more opcodes available. Decide what to do with the room later. |
|
Possible canidates for opcode compression include |
* Push immediate (room for 3 sub-opcodes) |
* push and pop reg (room for 7 sub-opcodes each) |
* equals 0 and not equals 0 (room for 7 sub-opcodes each) |
* Set TR and Reset TR (room for 64 opcodes each) |
* increment and decrement reg (room for 7 opcodes each) |
* enable and disable carry over (room for 7 opcodes each) |
* set register bank 0 and 1 (room for 64 opcodes each) |
* equals 0 and not equals 0 (room for 7 sub-opcodes each) (not doing that because it'd screw with the easy ALU code |
|
|
0 -nop (doesn't do a thing) |
1 -move immediate (only uses first byte) |
2 -move |
3 -push |
4 -push immediate |
5 -push and move (or call when acting on ip) |
6 -compare (is less than, is less than or equal, is greater than, is greater than or equal, is equal, is not equal) (6 conditions room for 2 more in extra) |
7 -add |
8 -subtract |
9 -bitwise operations (xor, or, and, shift right, shift left, not) |
|
x -multiply (if room) |
x -divide |
|
|
conditionals |
0 -- always |
1 -- only if true |
for only if false, there should basically be another compare or if applicable an always afterwards |
|
push |
pop |
move |
add |
sub |
|
limitations that shouldn't be passed with instructions |
* Doing 2 memory references |
* pushing a memory reference (equates to 2 memory references) |
|
Note it is possible however to read and write 16bits at one time to the memory to consecutive addresses. |
Note it is possible however to read and write 16bits at one time to the memory to consecutive addresses that are 16-bit aligned. |
|
|
segments: |
240,3 → 241,54
10100 Add |
10101 Subtract |
|
|
|
Alignment restrictions: |
In general, their is very few times that a full 16-bit read or 16-bit write is done. These are the times: |
|
* Extended push |
* Extended pop |
* instruction fetch |
|
Because of this, and because I want for 2 clock cycles to be the longest instruction, I must place some alignment restrictions on the CPU |
So, IP must be aligned to a 16-bit address (must be an even number). And SP must also be aligned to a 16-bit address. |
Though I don't plan on putting any "real" restriction to setting it to an odd address, nothing will actually work right. |
|
Stack Details: |
Because of the need for 16-bit writes and reads of the stack, even though we're usually only using 8-bit values, we end up pushing 2 bytes at one time always. |
Stack is oppositely done from the 8086. push X will move X to SS:SP and then increment SP by 2. |
Let's take an example program: |
--SS is 0 |
mov sp, 10 |
push 0xff |
|
after this, 0x00FF will be moved to SS:SP (0x0010) and then sp will be incremented by 2. If we push an 8-bit value, the value is put in the least-significant byte, and the MSB is 0 |
|
|
|
On Reset: |
|
On reset, all general registers are set to 0 |
CS is set to 1, IP is set to 0. SS is set to 2 and SP is set to 0. |
Carryover is set on CS and not set on SS. DS and ES is 0. TR is false. |
Register bank 0 is selected. |
|
|
|
|
Implemented opcode list: |
legend: |
r = register choice |
C = conditional portion |
s = segment register choice |
i = immediate data |
|
0000 rrrC iiii iiii |
mov reg, immediate |
|
|
|
|
|
|
|