OpenCores
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
 
 
 
 
 
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.