URL
https://opencores.org/ocsvn/tinycpu/tinycpu/trunk
Subversion Repositories tinycpu
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 38 to Rev 39
- ↔ Reverse comparison
Rev 38 → Rev 39
/tinycpu/trunk/testbench/top_tb.vhd
71,8 → 71,10
variable err_cnt: integer :=0; |
begin |
-- hold reset state for 100 ns. |
Reset <= '0'; |
wait for 10 ns; |
Reset <= '1'; |
wait for 20 ns; |
wait for 200 ns; |
Hold <= '1'; |
wait for 10 ns; |
assert (HoldAck ='1') report "HoldAck not becoming high" severity error; |
/tinycpu/trunk/src/bootrom.vhd
0,0 → 1,34
|
library ieee; |
use ieee.std_logic_1164.all; |
use IEEE.NUMERIC_STD.ALL; |
|
entity bootrom is |
port (CLK : in std_logic; |
EN : in std_logic; |
ADDR : in std_logic_vector(4 downto 0); |
DATA : out std_logic_vector(15 downto 0)); |
end bootrom; |
|
architecture syn of bootrom is |
constant ROMSIZE: integer := 64; |
type ROM_TYPE is array(ROMSIZE/2-1 downto 0) of std_logic_vector(15 downto 0); |
signal ROM: ROM_TYPE := (x"0801", x"0a01", x"58a3", x"0600", x"0402", x"5063", x"4040", x"3007", x"1701", x"3006", x"1700", x"0e16", |
x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000", x"0000"); |
signal rdata : std_logic_vector(15 downto 0); |
begin |
|
rdata <= ROM(to_integer(unsigned(ADDR))); |
|
process (CLK) |
begin |
if (CLK'event and CLK = '1') then |
if (EN = '1') then |
DATA <= rdata; |
end if; |
end if; |
end process; |
|
end syn; |
|
|
/tinycpu/trunk/src/top.vhd
63,6 → 63,14
DebugR0: out std_logic_vector(7 downto 0) |
); |
end component; |
component bootrom is |
port( |
CLK : in std_logic; |
EN : in std_logic; |
ADDR : in std_logic_vector(4 downto 0); |
DATA : out std_logic_vector(15 downto 0) |
); |
end component; |
signal cpuaddr: std_logic_vector(15 downto 0); |
signal cpuww: std_logic; |
signal cpuwe: std_logic; |
78,6 → 86,13
signal MemWriteEnable: std_logic; |
signal MemDataIn: std_logic_vector(15 downto 0); |
signal MemDataOut: std_logic_vector(15 downto 0); |
|
signal BootAddress: std_logic_vector(4 downto 0); |
signal BootDataIn: std_logic_vector(15 downto 0); |
signal BootDataOut: std_logic_vector(15 downto 0); |
signal BootDone: std_logic; |
constant ROMSIZE: integer := 64; |
signal counter: std_logic_vector(4 downto 0); |
begin |
cpu: core port map ( |
MemAddr => cpuaddr, |
104,12 → 119,35
DataOut => MemDataOut, |
Port0 => Port0 |
); |
|
MemAddress <= cpuaddr when DMA='0' else Address; |
MemWriteWord <= cpuww when DMA='0' else '1'; |
MemWriteEnable <= cpuwe when DMA='0' else WriteEnable; |
MemDataIn <= cpumemout when DMA='0' else Data when WriteEnable='1' else "ZZZZZZZZZZZZZZZZ"; |
rom: bootrom port map( |
clk => clock, |
EN => '1', |
Addr => BootAddress, |
Data => BootDataOut |
); |
MemAddress <= cpuaddr when (DMA='0' and Reset='0') else "00000001000" & BootAddress when (Reset='1' and DMA='0') else Address; |
MemWriteWord <= cpuww when DMA='0' and Reset='0' else '1' when Reset='1' and DMA='0' else '1'; |
MemWriteEnable <= cpuwe when DMA='0' and Reset='0' else'1' when Reset='1' and DMA='0' else WriteEnable; |
MemDataIn <= cpumemout when DMA='0' and Reset='0' else Data when WriteEnable='1' else BootDataIn when Reset='1' and DMA='0' else "ZZZZZZZZZZZZZZZZ"; |
cpumemin <= MemDataOut; |
Data <= MemDataOut when DMA='1' and WriteEnable='0' else "ZZZZZZZZZZZZZZZZ"; |
|
Data <= MemDataOut when DMA='1' and Reset='0' and WriteEnable='0' else "ZZZZZZZZZZZZZZZZ"; |
|
bootload: process(Clock, Reset) |
begin |
if rising_edge(clock) then |
if Reset='0' then |
counter <= "00000"; |
BootDone <= '0'; |
elsif Reset='1' and BootDone='0' then |
BootAddress <= counter; |
BootDataIn <= BootDataOut; |
counter <= std_logic_vector(unsigned(counter) + 1); |
if to_integer(unsigned(counter))>=(ROMSIZE/2-1) then |
BootDone <= '1'; |
end if; |
else |
|
end if; |
end if; |
end process; |
end Behavioral; |
/tinycpu/trunk/docs/design.md.txt
6,7 → 6,17
4. use a small amount of "rich" instructions to do powerful things |
5. 1 instruction per clock cycle |
|
I/O: |
I/O has been decided to use a memory mapped approach. |
Howto: |
|
Basically, absolute address 0-32 is reserved for ports. Right now there is only a Port0. This port is 8-bits long. |
Each even address is the port address. Each odd address is the bitmap for write or read for the port below it. |
|
So to make ports 7 to 4 write and 3 to 0 read, you'd assign 0xF0 to address 0x0001 |
|
|
|
BIG CHANGE: |
So, apparently making a single-cycle CPU is extremely hard... so instead, we'll be striving for a 2-cycle CPU. |
Usual cycles: |
347,3 → 357,7
010: mov reg, [reg] |
011: mov [reg], reg |
|
|
|
|
|
/tinycpu/trunk/docs/assembler.txt
0,0 → 1,55
The assembler is really a big hack job. Basically, I didn't want to write a complex assembler in C or C++, so I decided to use Ruby's power as a DSL to create an assembler. |
So ruby does all the heavy syntax stuff, and I just do a bit more meta-programming than anyone probably should. |
|
Anyway, Because of how it's made, you can of course use Ruby for any kind of assembler generation such as loops or whatever. Keep in mind, that after the program file runs, it outputs machine code though.. |
|
A simple example file: |
|
---- |
require "asm.rb" |
|
mov r0, 0x0F |
mov r1, 0x1C |
and r0, r1 |
|
----- |
|
And you run it by doing something like `ruby MyAssemblyFile.rb`. |
|
If you're just wanting to write some assembly code and not worry about how my assembler works, then it's quite simple. |
Just use the command above, make sure to use `require "asm.rb"` and make sure that asm.rb is in your working directory. |
|
The asembler is definitely an `intel` style assembler. By that, I mean target registers are on the left, and source registers are on the right. |
However, because of our unique CPU architecture, there are some interesting looking constructs. |
|
For instance, to use a block as `Only execute if TR is set` you'd use: |
|
mov r0, 10 |
mov r1, 20 |
cmpgt r0, r1 #reads as TR=r0 > r1 |
if_tr_set{ |
mov r0, 40 |
} |
|
|
|
|
Also, for here is a quick lookup table for the assembly words that aren't obvious: |
|
rsh -- right shift |
lsh -- left shift |
rro -- right rotate |
lro -- left rotate |
cmpgt -- compare greater than |
cmpgte -- compare greater than or equal |
cmplt -- compare less than |
cmplte -- compare less than or equal |
cmpeq -- compare equal |
cmpneq -- compare not equal |
|
To avoid all sorts of hell with Ruby, some assembler words must be suffixed with a _ |
These are listed below: |
or_ |
and_ |
xor_ |
not_ |
/tinycpu/trunk/assembler/asm.rb
1,6 → 1,6
PREFIX = "MemIn <= x\""; |
SUFFIX = "\";"; |
SEPERATOR = "\n"; |
PREFIX = "x\""; |
SUFFIX = "\""; |
SEPERATOR = ", "; |
|
|
|
48,7 → 48,27
|
$iftr = 0; #0 for no condition, 1 for if TR, 2 for if not TR |
$useextra = 0; |
$position = 0; |
|
def set_cond(o1, o2) |
if $iftr==0 then |
o1.cond=0; |
o2.cond=0; |
elsif $iftr==1 then |
o1.cond=1; |
o2.cond=0; |
else |
o1.cond=0; |
o2.cond=1; |
end |
end |
def output_op(value) |
printf PREFIX + value + SUFFIX; |
printf SEPERATOR; |
$position+=2; |
end |
|
|
def mov_r8_imm8(reg,imm) |
o = OpcodeByte1.new(); |
o.op = 0; |
58,8 → 78,7
else |
raise "if_tr_notset is not allowed with this opcode"; |
end |
puts PREFIX + o.to_hex + imm.to_s(16) + SUFFIX; |
puts SEPERATOR; |
output_op(o.to_hex.rjust(2,"0") + imm.to_s(16).rjust(2,"0")) |
end |
def mov_rm8_imm8(reg,imm) |
o=OpcodeByte1.new(); |
70,10 → 89,92
else |
raise "if_tr_notset is not allowed with this opcode"; |
end |
puts PREFIX + o.to_hex + imm.to_s(16) + SUFFIX; |
puts SEPERATOR; |
output_op(o.to_hex.rjust(2,"0") + imm.to_s(16).rjust(2,"0")); |
end |
def |
|
def do_group_reg_reg(opcode,group,reg1,reg2) |
o1 = OpcodeByte1.new() |
o1.op=opcode; |
o1.register=reg1; |
o2 = OpcodeByte2.new() |
o2.useextra=$useextra; |
o2.reg2=reg2; |
o2.reg3=OpcodeOption.new(group); #opcode group |
set_cond(o1,o2) |
output_op(o1.to_hex.rjust(2,"0") + o2.to_hex.rjust(2,"0")) |
end |
def do_subgroup_reg(opcode,group,subgroup,reg1) |
o1 = OpcodeByte1.new() |
o1.op=opcode; |
o1.register=reg1; |
o2 = OpcodeByte2.new() |
o2.useextra=$useextra; |
o2.reg2=OpcodeOption.new(subgroup); |
o2.reg3=OpcodeOption.new(group); #opcode group |
set_cond(o1,o2) |
output_op(o1.to_hex.rjust(2,"0") + o2.to_hex.rjust(2,"0")) |
end |
|
def and_reg_reg(reg1, reg2) |
do_group_reg_reg(4,0,reg1,reg2) |
end; |
def or_reg_reg(reg1, reg2) |
do_group_reg_reg(4,1,reg1,reg2) |
end; |
def xor_reg_reg(reg1, reg2) |
do_group_reg_reg(4,2,reg1,reg2) |
end; |
def not_reg_reg(reg1, reg2) |
do_group_reg_reg(4,3,reg1,reg2) |
end; |
def lsh_reg_reg(reg1, reg2) |
do_group_reg_reg(4,4,reg1,reg2) |
end; |
def rsh_reg_reg(reg1, reg2) |
do_group_reg_reg(4,5,reg1,reg2) |
end; |
def lro_reg_reg(reg1, reg2) |
do_group_reg_reg(4,6,reg1,reg2) |
end; |
def rro_reg_reg(reg1, reg2) |
do_group_reg_reg(4,7,reg1,reg2) |
end; |
#comparisons |
def cmpgt_reg_reg(reg1, reg2) |
do_group_reg_reg(3,0,reg1,reg2) |
end; |
def cmpgte_reg_reg(reg1, reg2) |
do_group_reg_reg(3,1,reg1,reg2) |
end; |
def cmplt_reg_reg(reg1, reg2) |
do_group_reg_reg(3,2,reg1,reg2) |
end; |
def cmplte_reg_reg(reg1, reg2) |
do_group_reg_reg(3,3,reg1,reg2) |
end; |
def cmpeq_reg_reg(reg1, reg2) |
do_group_reg_reg(3,4,reg1,reg2) |
end; |
def cmpneq_reg_reg(reg1, reg2) |
do_group_reg_reg(3,5,reg1,reg2) |
end; |
def cmpeq_reg_0(reg1) |
do_group_reg_reg(3,6,reg1,Register8.new(0)) #last arg isn't used |
end; |
def cmpneq_reg_0(reg1) |
do_group_reg_reg(3,7,reg1,Register8.new(0)) |
end; |
|
def mov_reg_mreg(reg1, reg2) |
do_group_reg_reg(5,2,reg1,reg2) |
end |
def mov_mreg_reg(reg1, reg2) |
do_group_reg_reg(5,3,reg1,reg2) |
end |
def mov_reg_reg(reg1, reg2) |
do_group_reg_reg(5,1,reg1,reg2) |
end |
|
|
|
def mov(arg1,arg2) |
80,16 → 181,147
if arg1.kind_of? Register8 and arg2.kind_of? Integer and arg2<0x100 then |
mov_r8_imm8 arg1,arg2 |
elsif arg1.kind_of? Array and arg2.kind_of? Integer and arg2<0x100 then |
if arg1.length>1 or arg1.length<1 then |
if arg1.length>1 or arg1.length<1 or not arg1[0].kind_of? Register8 then |
raise "memory reference is not correct. Only a register is allowed"; |
end |
reg=arg1[0]; |
mov_rm8_imm8 reg, arg2 |
elsif arg1.kind_of? Array and arg2.kind_of? Register8 then |
if arg1.length>1 or arg1.length<1 or not arg1[0].kind_of? Register8 then |
raise "memory reference is not correct. Only a register is allowed"; |
end |
mov_mreg_reg arg1[0], arg2 |
elsif arg1.kind_of? Register8 and arg2.kind_of? Array then |
if arg2.length>1 or arg2.length<1 or not arg2[0].kind_of? Register8 then |
raise "memory reference is not correct. Only a register is allowed"; |
end |
mov_mreg_reg arg1,arg2[0] |
elsif arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
mov_reg_reg arg1, arg2 |
else |
raise "No suitable mov opcode found"; |
end |
|
end |
def and_(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
and_reg_reg arg1,arg2 |
else |
raise "No suitable and opcode found"; |
end |
end |
def or_(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
or_reg_reg arg1,arg2 |
else |
raise "No suitable or opcode found"; |
end |
end |
def xor_(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
xor_reg_reg arg1,arg2 |
else |
raise "No suitable xor opcode found"; |
end |
end |
def not_(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
not_reg_reg arg1,arg2 |
else |
raise "No suitable not opcode found"; |
end |
end |
def rsh(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
rsh_reg_reg arg1,arg2 |
else |
raise "No suitable rsh opcode found"; |
end |
end |
def lsh(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
lsh_reg_reg arg1,arg2 |
else |
raise "No suitable lsh opcode found"; |
end |
end |
def rro(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
rro_reg_reg arg1,arg2 |
else |
raise "No suitable rro opcode found"; |
end |
end |
def lro(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
lro_reg_reg arg1,arg2 |
else |
raise "No suitable lro opcode found"; |
end |
end |
|
def cmpgt(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
cmpgt_reg_reg arg1,arg2 |
else |
raise "No suitable cmpgt opcode found"; |
end |
end |
def cmpgte(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
cmpgte_reg_reg arg1,arg2 |
else |
raise "No suitable cmpgte opcode found"; |
end |
end |
def cmplt(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
cmplt_reg_reg arg1,arg2 |
else |
raise "No suitable cmplt opcode found"; |
end |
end |
def cmplte(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
cmplte_reg_reg arg1,arg2 |
else |
raise "No suitable cmplte opcode found"; |
end |
end |
def cmpeq(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
cmpeq_reg_reg arg1,arg2 |
elsif arg1.kind_of? Register8 and arg2.kind_of? Integer and arg2==0 then |
cmpeq_reg_0 arg1 |
else |
raise "No suitable cmpeq opcode found"; |
end |
end |
def cmpneq(arg1,arg2) |
if arg1.kind_of? Register8 and arg2.kind_of? Register8 then |
cmpneq_reg_reg arg1,arg2 |
elsif arg1.kind_of? Register8 and arg2.kind_of? Integer and arg2==0 then |
cmpneq_reg_0 arg1 |
else |
raise "No suitable cmpneq opcode found"; |
end |
end |
|
def Label |
attr_accessor :name, :pos |
def initialize(name, pos) |
@name=name; |
@pos=pos; |
end |
end |
$labellist={} |
def label(name) |
$labellist[name.to_s]=$position; |
end |
def lbl(name) |
$labellist[name.to_s]=$position; |
end |
|
|
def if_tr_set |
$iftr = 1 |
yield |
108,8 → 340,32
|
|
#test code follows. Only do it here for convenience.. real usage should prefix assembly files with `require "asm.rb"` |
|
|
#port0(0) is LED port0(1) is a button |
|
mov r4, 1 |
mov r5, 0x01 #the port bitmask |
mov [r4],r5 |
mov r3, 0 |
mov r2, 0x02 |
#poll for button |
label :loop |
mov r0, [r3] |
and_ r0, r2 #isolate just the button at pin 2 |
cmpneq r0, 0 |
if_tr_set{ |
mov r1,0x10 |
mov [r3], 0x01 |
} |
mov r1,0x20 |
mov [r1], 0x50 |
cmpeq r0,0 |
if_tr_set{ |
mov [r3], 0x00 |
} |
mov ip, lbl(:loop) |
|
printf("\n"); |
while $position<64 |
printf("x\"0000\", ") |
$position+=2; |
end |
puts "\nsize:" + $position.to_s |