diff options
Diffstat (limited to '')
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 7 | ||||
| -rwxr-xr-x | asm.rb | 196 | ||||
| -rw-r--r-- | boards/decode/decode.kicad_pcb | 1 | ||||
| -rw-r--r-- | boards/decode/decode.pro | 33 | ||||
| -rw-r--r-- | boards/decode/decode.sch | 4 | ||||
| -rw-r--r-- | fib.s | 21 | ||||
| -rw-r--r-- | insts.rb | 228 | ||||
| -rw-r--r-- | modules.rb | 32 | ||||
| -rw-r--r-- | sim/.gitignore | 2 | ||||
| -rw-r--r-- | sim/Makefile | 12 | ||||
| -rw-r--r-- | sim/alu.sv | 76 | ||||
| -rw-r--r-- | sim/control.sv | 54 | ||||
| -rw-r--r-- | sim/decode.sv | 82 | ||||
| -rw-r--r-- | sim/main.cpp | 61 | ||||
| -rw-r--r-- | sim/memory.sv | 35 | ||||
| -rw-r--r-- | sim/pc.sv | 36 | ||||
| -rw-r--r-- | sim/rf.sv | 39 | ||||
| -rw-r--r-- | sim/tmp.sv | 41 | ||||
| -rw-r--r-- | sim/top.sv | 27 | ||||
| -rw-r--r-- | sim/uart.sv | 47 | ||||
| -rw-r--r-- | sim/urom.sv | 19 | ||||
| -rwxr-xr-x | uasm.rb | 174 | ||||
| -rw-r--r-- | vectors.rb | 7 |
24 files changed, 1235 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2e7327 --- /dev/null +++ b/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| /out | |||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..31000b8 --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | run: out/image.hex | ||
| 2 | $(MAKE) -C sim | ||
| 3 | .PHONY: run | ||
| 4 | |||
| 5 | out/image.hex: fib.s $(wildcard *.rb) | ||
| 6 | ./uasm.rb | ||
| 7 | ./asm.rb $< | expand -t 24 > $@ | ||
| @@ -0,0 +1,196 @@ | |||
| 1 | #!/usr/bin/ruby -w | ||
| 2 | |||
| 3 | $insts = {} | ||
| 4 | |||
| 5 | $next_opcode = 0 | ||
| 6 | |||
| 7 | def inst(pattern, vector=false, &code) | ||
| 8 | return if vector | ||
| 9 | throw "instruction with this pattern already defined" if $insts.member?(pattern) | ||
| 10 | $insts[pattern] = $next_opcode | ||
| 11 | $next_opcode += 1 | ||
| 12 | end | ||
| 13 | |||
| 14 | load "insts.rb" | ||
| 15 | |||
| 16 | $labels = {} | ||
| 17 | $pc = 0 | ||
| 18 | |||
| 19 | ARGF.each_line do | line | | ||
| 20 | line.chomp! | ||
| 21 | line.gsub!(/^\s+/, "") | ||
| 22 | line.gsub!(/#.*/, "") | ||
| 23 | line.gsub!(/\s+$/, "") | ||
| 24 | |||
| 25 | next if line == "" | ||
| 26 | |||
| 27 | if line =~ /^([a-zA-Z_][a-zA-Z0-9_]*):$/ | ||
| 28 | $labels[$1] = $pc | ||
| 29 | $stdout.write("\t// #{line}\n") | ||
| 30 | next | ||
| 31 | elsif line =~ /^\.origin\s+((?:0x[0-9a-fA-F]+)?(?:\d+))$/ | ||
| 32 | $pc = $1.to_i(0) | ||
| 33 | $stdout.write("@#{$pc.to_s(16)}\t// #{line}\n") | ||
| 34 | next | ||
| 35 | elsif line =~ /^\.bytes?\s+(.*)$/ | ||
| 36 | data = $1.split(/\s+/) | ||
| 37 | data.map { | x | throw "invalid byte #{x}" unless x =~ /^-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+))$/ } | ||
| 38 | data = data.map { | x | x.to_i(0) } | ||
| 39 | data = data.map { | x | x < 0 ? x + 0x100 : x } | ||
| 40 | data.map { | x | throw "invalid byte 0x#{x.to_s(16)}" unless x >= 0 && x <= 0xff } | ||
| 41 | data = data.map { | x | x.to_s(16).rjust(2, "0") } | ||
| 42 | $pc += data.size | ||
| 43 | $stdout.write("#{data.join(" ")}\t// #{line}\n") | ||
| 44 | next | ||
| 45 | elsif line =~ /^\.shorts?\s+(.*)$/ | ||
| 46 | data = $1.split(/\s+/) | ||
| 47 | data.map { | x | throw "invalid short #{x}" unless x =~ /^-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+))$/ } | ||
| 48 | data = data.map { | x | x.to_i(0) } | ||
| 49 | data = data.map { | x | x < 0 ? x + 0x10000 : x } | ||
| 50 | data.map { | x | throw "invalid short 0x#{x.to_s(16)}" unless x >= 0 && x <= 0xffff } | ||
| 51 | data = data.flat_map { | x | [x & 0xff, x >> 8] } | ||
| 52 | data = data.map { | x | x.to_s(16).rjust(2, "0") } | ||
| 53 | $pc += data.size | ||
| 54 | $stdout.write("#{data.join(" ")}\t// #{line}\n") | ||
| 55 | next | ||
| 56 | elsif line =~ /^\.longs?\s+(.*)$/ | ||
| 57 | data = $1.split(/\s+/) | ||
| 58 | data.map { | x | throw "invalid long #{x}" unless x =~ /^-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+))$/ } | ||
| 59 | data = data.map { | x | x.to_i(0) } | ||
| 60 | data = data.map { | x | x < 0 ? x + 0x100000000 : x } | ||
| 61 | data.map { | x | throw "invalid long 0x#{x.to_s(16)}" unless x >= 0 && x <= 0xffffffff } | ||
| 62 | data = data.flat_map { | x | [x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, x >> 24] } | ||
| 63 | data = data.map { | x | x.to_s(16).rjust(2, "0") } | ||
| 64 | $pc += data.size | ||
| 65 | $stdout.write("#{data.join(" ")}\t// #{line}\n") | ||
| 66 | next | ||
| 67 | elsif line =~ /^\.quads?\s+(.*)$/ | ||
| 68 | data = $1.split(/\s+/) | ||
| 69 | data.map { | x | throw "invalid quad #{x}" unless x =~ /^-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+))$/ } | ||
| 70 | data = data.map { | x | x.to_i(0) } | ||
| 71 | data = data.map { | x | x < 0 ? x + 0x10000000000000000 : x } | ||
| 72 | data.map { | x | throw "invalid quad 0x#{x.to_s(16)}" unless x >= 0 && x <= 0xffffffffffffffff } | ||
| 73 | data = data.flat_map { | x | [x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff, (x >> 32) & 0xff, (x >> 40) & 0xff, (x >> 48) & 0xff, x >> 56] } | ||
| 74 | data = data.map { | x | x.to_s(16).rjust(2, "0") } | ||
| 75 | $pc += data.size | ||
| 76 | $stdout.write("#{data.join(" ")}\t// #{line}\n") | ||
| 77 | next | ||
| 78 | end | ||
| 79 | |||
| 80 | parts = line.split(/\s+/) | ||
| 81 | |||
| 82 | patterns = [parts.shift] | ||
| 83 | values = [] | ||
| 84 | parts.each do | part | | ||
| 85 | case part | ||
| 86 | when /^(-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+)))$/ | ||
| 87 | patterns << "#" | ||
| 88 | values << $1.to_i(0) | ||
| 89 | when /^([a-zA-Z_][a-zA-Z0-9_]+)$/ | ||
| 90 | throw "unknown label #{$1}" unless $labels.member?($1) | ||
| 91 | patterns << "#" | ||
| 92 | values << $labels[$1] | ||
| 93 | when /^\$(-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+)))$/ | ||
| 94 | patterns << "$" | ||
| 95 | values << $1.to_i(0) | ||
| 96 | when /^\[(-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+)))\]$/ | ||
| 97 | patterns << "[#]" | ||
| 98 | values << $1.to_i(0) | ||
| 99 | when /^\[([a-zA-Z_][a-zA-Z0-9_]+)\]$/ | ||
| 100 | throw "unknown label #{$1}" unless $labels.member?($1) | ||
| 101 | patterns << "[#]" | ||
| 102 | values << $labels[$1] | ||
| 103 | when /^\[\$(-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+)))\]$/ | ||
| 104 | patterns << "[$]" | ||
| 105 | values << $1.to_i(0) | ||
| 106 | else | ||
| 107 | throw "invalid part #{part}" | ||
| 108 | end | ||
| 109 | end | ||
| 110 | |||
| 111 | pattern = patterns.join(" ") | ||
| 112 | throw "unknown instruction pattern #{pattern}" unless $insts.member?(pattern) | ||
| 113 | opcode = $insts[pattern] | ||
| 114 | |||
| 115 | # Encoding format: | ||
| 116 | # +-+-+-+-+-+-+-+-+ | ||
| 117 | # |M|I|SEL| SHIFT | | ||
| 118 | # +-+-+-+-+-+-+-+-+ | ||
| 119 | # M = More data after this | ||
| 120 | # I = Invert this field before shifting | ||
| 121 | # SEL = Field selector (0=opcode, 2=A, 3=B) | ||
| 122 | # SHIFT = Data to shift in | ||
| 123 | |||
| 124 | # This gives us these common first nibbles: | ||
| 125 | # 0 last opcode byte | ||
| 126 | # 8 extended opcode byte | ||
| 127 | # A operand A byte | ||
| 128 | # B operand B byte | ||
| 129 | # E operand A byte (with inversion) | ||
| 130 | # F operand B byte (with inversion) | ||
| 131 | |||
| 132 | space = false | ||
| 133 | values.each_with_index do | value, idx | | ||
| 134 | inverted = value < 0 | ||
| 135 | if inverted | ||
| 136 | value *= -1 | ||
| 137 | value -= 1 | ||
| 138 | end | ||
| 139 | nibbles = [] | ||
| 140 | while value != 0 | ||
| 141 | nibbles << (value & 0xf).to_s(16) | ||
| 142 | value = value >> 4 | ||
| 143 | end | ||
| 144 | if inverted and nibbles.empty? | ||
| 145 | nibbles << "f" | ||
| 146 | end | ||
| 147 | nibbles.reverse! | ||
| 148 | if idx == 0 | ||
| 149 | if inverted | ||
| 150 | $stdout.write(" ") if space | ||
| 151 | $stdout.write("e#{nibbles.shift}") | ||
| 152 | $pc += 1 | ||
| 153 | space = true | ||
| 154 | end | ||
| 155 | nibbles.each do | nibble | | ||
| 156 | $stdout.write(" ") if space | ||
| 157 | $stdout.write("a#{nibble}") | ||
| 158 | $pc += 1 | ||
| 159 | space = true | ||
| 160 | end | ||
| 161 | elsif idx == 1 | ||
| 162 | if inverted | ||
| 163 | $stdout.write(" ") if space | ||
| 164 | $stdout.write("f#{nibbles.shift}") | ||
| 165 | $pc += 1 | ||
| 166 | space = true | ||
| 167 | end | ||
| 168 | nibbles.each do | nibble | | ||
| 169 | $stdout.write(" ") if space | ||
| 170 | $stdout.write("b#{nibble}") | ||
| 171 | $pc += 1 | ||
| 172 | space = true | ||
| 173 | end | ||
| 174 | else | ||
| 175 | throw "don't know how to encode this many fields" | ||
| 176 | end | ||
| 177 | end | ||
| 178 | |||
| 179 | nibbles = [] | ||
| 180 | while opcode != 0 | ||
| 181 | nibbles << (opcode & 0xf).to_s(16) | ||
| 182 | opcode = opcode >> 4 | ||
| 183 | end | ||
| 184 | nibbles << "0" if nibbles.empty? | ||
| 185 | last = nibbles.shift | ||
| 186 | nibbles.reverse! | ||
| 187 | nibbles.each do | nibble | | ||
| 188 | $stdout.write(" ") if space | ||
| 189 | $stdout.write("8#{nibble}") | ||
| 190 | $pc += 1 | ||
| 191 | space = true | ||
| 192 | end | ||
| 193 | $stdout.write(" ") if space | ||
| 194 | $stdout.write("0#{last}\t// #{line}\n") | ||
| 195 | $pc += 1 | ||
| 196 | end | ||
diff --git a/boards/decode/decode.kicad_pcb b/boards/decode/decode.kicad_pcb new file mode 100644 index 0000000..02c8ecb --- /dev/null +++ b/boards/decode/decode.kicad_pcb | |||
| @@ -0,0 +1 @@ | |||
| (kicad_pcb (version 4) (host kicad "dummy file") ) | |||
diff --git a/boards/decode/decode.pro b/boards/decode/decode.pro new file mode 100644 index 0000000..152769c --- /dev/null +++ b/boards/decode/decode.pro | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | update=22/05/2015 07:44:53 | ||
| 2 | version=1 | ||
| 3 | last_client=kicad | ||
| 4 | [general] | ||
| 5 | version=1 | ||
| 6 | RootSch= | ||
| 7 | BoardNm= | ||
| 8 | [pcbnew] | ||
| 9 | version=1 | ||
| 10 | LastNetListRead= | ||
| 11 | UseCmpFile=1 | ||
| 12 | PadDrill=0.600000000000 | ||
| 13 | PadDrillOvalY=0.600000000000 | ||
| 14 | PadSizeH=1.500000000000 | ||
| 15 | PadSizeV=1.500000000000 | ||
| 16 | PcbTextSizeV=1.500000000000 | ||
| 17 | PcbTextSizeH=1.500000000000 | ||
| 18 | PcbTextThickness=0.300000000000 | ||
| 19 | ModuleTextSizeV=1.000000000000 | ||
| 20 | ModuleTextSizeH=1.000000000000 | ||
| 21 | ModuleTextSizeThickness=0.150000000000 | ||
| 22 | SolderMaskClearance=0.000000000000 | ||
| 23 | SolderMaskMinWidth=0.000000000000 | ||
| 24 | DrawSegmentWidth=0.200000000000 | ||
| 25 | BoardOutlineThickness=0.100000000000 | ||
| 26 | ModuleOutlineThickness=0.150000000000 | ||
| 27 | [cvpcb] | ||
| 28 | version=1 | ||
| 29 | NetIExt=net | ||
| 30 | [eeschema] | ||
| 31 | version=1 | ||
| 32 | LibDir= | ||
| 33 | [eeschema/libraries] | ||
diff --git a/boards/decode/decode.sch b/boards/decode/decode.sch new file mode 100644 index 0000000..fff8c68 --- /dev/null +++ b/boards/decode/decode.sch | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | EESchema Schematic File Version 2 | ||
| 2 | EELAYER 25 0 | ||
| 3 | EELAYER END | ||
| 4 | $EndSCHEMATC | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | mov $0 0 | ||
| 2 | mov $1 1 | ||
| 3 | mov $2 0x8000 | ||
| 4 | |||
| 5 | loop: | ||
| 6 | |||
| 7 | mov [$2] $0 | ||
| 8 | add $2 1 | ||
| 9 | |||
| 10 | tx $0 | ||
| 11 | |||
| 12 | mov $-1 $0 | ||
| 13 | add $-1 $1 | ||
| 14 | mov $0 $1 | ||
| 15 | mov $1 $-1 | ||
| 16 | |||
| 17 | mov $-1 $2 | ||
| 18 | eq $-1 0x800a | ||
| 19 | jz $-1 loop | ||
| 20 | |||
| 21 | hlt | ||
diff --git a/insts.rb b/insts.rb new file mode 100644 index 0000000..bc9c156 --- /dev/null +++ b/insts.rb | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | inst "hlt" do | ||
| 2 | @control.halt | ||
| 3 | end | ||
| 4 | |||
| 5 | inst "mov $ #" do | ||
| 6 | uop { @decode.outaddr_a; @decode.outdata_b; @rf.store } | ||
| 7 | end | ||
| 8 | |||
| 9 | inst "mov $ $" do | ||
| 10 | uop { @decode.outaddr_b; @rf.outdata; @tmp0.loaddata } | ||
| 11 | uop { @decode.outaddr_a; @tmp0.outdata; @rf.store } | ||
| 12 | end | ||
| 13 | |||
| 14 | inst "mov [#] #" do | ||
| 15 | uop { @decode.outaddr_a; @decode.outdata_b; @memory.store } | ||
| 16 | end | ||
| 17 | |||
| 18 | inst "mov [#] $" do | ||
| 19 | uop { @decode.outaddr_b; @rf.outdata; @tmp0.loaddata } | ||
| 20 | uop { @decode.outaddr_a; @tmp0.outdata; @memory.store } | ||
| 21 | end | ||
| 22 | |||
| 23 | inst "mov [$] #" do | ||
| 24 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 25 | uop { @decode.outdata_b; @tmp0.outaddr; @memory.store } | ||
| 26 | end | ||
| 27 | |||
| 28 | inst "mov [$] $" do | ||
| 29 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 30 | uop { @decode.outaddr_b; @rf.outdata; @tmp1.loaddata } | ||
| 31 | uop { @tmp0.outaddr; @tmp1.outdata; @memory.store } | ||
| 32 | end | ||
| 33 | |||
| 34 | inst "mov $ [#]" do | ||
| 35 | uop { @decode.outaddr_b; @memory.outdata; @tmp0.loaddata } | ||
| 36 | uop { @decode.outaddr_a; @tmp0.outdata; @rf.store } | ||
| 37 | end | ||
| 38 | |||
| 39 | inst "mov $ [$]" do | ||
| 40 | uop { @decode.outaddr_b; @rf.outdata; @tmp0.loaddata } | ||
| 41 | uop { @tmp0.outaddr; @memory.outdata; @tmp1.loaddata } | ||
| 42 | uop { @decode.outaddr_a; @tmp1.outdata; @rf.store } | ||
| 43 | end | ||
| 44 | |||
| 45 | inst "skipz $" do | ||
| 46 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 47 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr($eom) } | ||
| 48 | uop { @decode.clear } | ||
| 49 | decode_loop = uip() | ||
| 50 | uop { @pc.increment; @pc.outaddr; @memory.outdata; @decode.decode } | ||
| 51 | uop { @decode.outdata_needmore; @control.set_uip_if_nonzero; constaddr(decode_loop) } | ||
| 52 | end | ||
| 53 | |||
| 54 | inst "skipnz $" do | ||
| 55 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 56 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr(uip()+2) } | ||
| 57 | uop { @control.set_uip; constaddr($eom) } | ||
| 58 | uop { @decode.clear } | ||
| 59 | decode_loop = uip() | ||
| 60 | uop { @pc.increment; @pc.outaddr; @memory.outdata; @decode.decode } | ||
| 61 | uop { @decode.outdata_needmore; @control.set_uip_if_nonzero; constaddr(decode_loop) } | ||
| 62 | end | ||
| 63 | |||
| 64 | inst "push $ #" do | ||
| 65 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 66 | uop { @tmp0.outaddr; @alu.sub; constdata(1) } | ||
| 67 | uop { @decode.outaddr_a; @alu.outdata; @rf.store } | ||
| 68 | uop { @decode.outdata_b; @alu.outaddr; @memory.store } | ||
| 69 | end | ||
| 70 | |||
| 71 | inst "push $ $" do | ||
| 72 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 73 | uop { @tmp0.outaddr; @alu.sub; constdata(1) } | ||
| 74 | uop { @decode.outaddr_a; @alu.outdata; @rf.store } | ||
| 75 | uop { @decode.outaddr_b; @rf.outdata; @tmp1.loaddata } | ||
| 76 | uop { @tmp1.outdata; @alu.outaddr; @memory.store } | ||
| 77 | end | ||
| 78 | |||
| 79 | inst "pop $ $" do | ||
| 80 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 81 | uop { @tmp0.outaddr; @alu.add; constdata(1) } | ||
| 82 | uop { @decode.outaddr_a; @alu.outdata; @rf.store } | ||
| 83 | uop { @alu.outaddr; @memory.outdata; @tmp0.loaddata } | ||
| 84 | uop { @decode.outaddr_b; @tmp0.outdata; @rf.store } | ||
| 85 | end | ||
| 86 | |||
| 87 | def alu(op) | ||
| 88 | inst "#{op} $ #" do | ||
| 89 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 90 | uop { @decode.outdata_b; @tmp0.outaddr; @alu.send(op) } | ||
| 91 | uop { @decode.outaddr_a; @alu.outdata; @rf.store } | ||
| 92 | end | ||
| 93 | |||
| 94 | inst "#{op} $ $" do | ||
| 95 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 96 | uop { @decode.outaddr_b; @rf.outdata; @tmp1.loaddata } | ||
| 97 | uop { @tmp0.outaddr; @tmp1.outdata; @alu.send(op) } | ||
| 98 | uop { @decode.outaddr_a; @alu.outdata; @rf.store } | ||
| 99 | end | ||
| 100 | end | ||
| 101 | |||
| 102 | alu :and | ||
| 103 | alu :or | ||
| 104 | alu :xor | ||
| 105 | alu :add | ||
| 106 | alu :sub | ||
| 107 | alu :cmp | ||
| 108 | alu :lshift | ||
| 109 | alu :rshift | ||
| 110 | |||
| 111 | def cmpbit(name, bit) | ||
| 112 | inst "#{name} $ #" do | ||
| 113 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 114 | uop { @decode.outdata_b; @tmp0.outaddr; @alu.cmp } | ||
| 115 | uop { @alu.outdata; @alu.and; constaddr(1<<bit) } | ||
| 116 | uop { @decode.outaddr_a; @alu.outdata; @rf.store } | ||
| 117 | end | ||
| 118 | |||
| 119 | inst "#{name} $ $" do | ||
| 120 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 121 | uop { @decode.outaddr_b; @rf.outdata; @tmp1.loaddata } | ||
| 122 | uop { @tmp0.outaddr; @tmp1.outdata; @alu.cmp } | ||
| 123 | uop { @alu.outdata; @alu.and; constaddr(1<<bit) } | ||
| 124 | uop { @decode.outaddr_a; @alu.outdata; @rf.store } | ||
| 125 | end | ||
| 126 | end | ||
| 127 | |||
| 128 | cmpbit :lt, 0 | ||
| 129 | cmpbit :eq, 1 | ||
| 130 | cmpbit :gt, 2 | ||
| 131 | cmpbit :lxor, 3 | ||
| 132 | cmpbit :lor, 4 | ||
| 133 | cmpbit :land, 5 | ||
| 134 | |||
| 135 | inst "jmp #" do | ||
| 136 | uop { @decode.outaddr_a; @pc.load } | ||
| 137 | end | ||
| 138 | |||
| 139 | inst "jmp $" do | ||
| 140 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 141 | uop { @tmp0.outaddr; @pc.load } | ||
| 142 | end | ||
| 143 | |||
| 144 | inst "jz $ #" do | ||
| 145 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 146 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr($eom) } | ||
| 147 | uop { @decode.outaddr_b; @pc.load } | ||
| 148 | end | ||
| 149 | |||
| 150 | inst "jz $ $" do | ||
| 151 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 152 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr($eom) } | ||
| 153 | uop { @decode.outaddr_b; @rf.outdata; @tmp0.loaddata } | ||
| 154 | uop { @tmp0.outaddr; @pc.load } | ||
| 155 | end | ||
| 156 | |||
| 157 | inst "jnz $ #" do | ||
| 158 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 159 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr(uip()+2) } | ||
| 160 | uop { @control.set_uip; constaddr($eom) } | ||
| 161 | uop { @decode.outaddr_b; @pc.load } | ||
| 162 | end | ||
| 163 | |||
| 164 | inst "jnz $ $" do | ||
| 165 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 166 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr(uip()+2) } | ||
| 167 | uop { @control.set_uip; constaddr($eom) } | ||
| 168 | uop { @decode.outaddr_b; @rf.outdata; @tmp0.loaddata } | ||
| 169 | uop { @tmp0.outaddr; @pc.load } | ||
| 170 | end | ||
| 171 | |||
| 172 | inst "rjmp #" do | ||
| 173 | uop { @decode.outdata_a; @pc.outaddr; @alu.add } | ||
| 174 | uop { @alu.outaddr; @pc.load } | ||
| 175 | end | ||
| 176 | |||
| 177 | inst "rjmp $" do | ||
| 178 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 179 | uop { @tmp0.outdata; @pc.outaddr; @alu.add } | ||
| 180 | uop { @alu.outaddr; @pc.load } | ||
| 181 | end | ||
| 182 | |||
| 183 | inst "rjz $ #" do | ||
| 184 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 185 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr($eom) } | ||
| 186 | uop { @decode.outdata_b; @pc.outaddr; @alu.add } | ||
| 187 | uop { @alu.outaddr; @pc.load } | ||
| 188 | end | ||
| 189 | |||
| 190 | inst "rjz $ $" do | ||
| 191 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 192 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr($eom) } | ||
| 193 | uop { @decode.outaddr_b; @rf.outdata; @tmp0.loaddata } | ||
| 194 | uop { @tmp0.outdata; @pc.outaddr; @alu.add } | ||
| 195 | uop { @alu.outaddr; @pc.load } | ||
| 196 | end | ||
| 197 | |||
| 198 | inst "rjnz $ #" do | ||
| 199 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 200 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr(uip()+2) } | ||
| 201 | uop { @control.set_uip; constaddr($eom) } | ||
| 202 | uop { @decode.outdata_b; @pc.outaddr; @alu.add } | ||
| 203 | uop { @alu.outaddr; @pc.load } | ||
| 204 | end | ||
| 205 | |||
| 206 | inst "rjnz $ $" do | ||
| 207 | uop { @decode.outaddr_a; @rf.outdata; @tmp0.loaddata } | ||
| 208 | uop { @tmp0.outdata; @control.set_uip_if_nonzero; constaddr(uip()+2) } | ||
| 209 | uop { @control.set_uip; constaddr($eom) } | ||
| 210 | uop { @decode.outaddr_b; @rf.outdata; @tmp0.loaddata } | ||
| 211 | uop { @tmp0.outdata; @pc.outaddr; @alu.add } | ||
| 212 | uop { @alu.outaddr; @pc.load } | ||
| 213 | end | ||
| 214 | |||
| 215 | inst "rx $" do | ||
| 216 | uop { @uart.outdata_rxempty; @control.set_uip_if_nonzero; constaddr(uip()) } | ||
| 217 | uop { @decode.outaddr_a; @uart.rx; @rf.store } | ||
| 218 | end | ||
| 219 | |||
| 220 | inst "tx $" do | ||
| 221 | uop { @uart.outdata_txfull; @control.set_uip_if_nonzero; constaddr(uip()) } | ||
| 222 | uop { @decode.outaddr_a; @rf.outdata; @uart.tx } | ||
| 223 | end | ||
| 224 | |||
| 225 | inst "tx #" do | ||
| 226 | uop { @uart.outdata_txfull; @control.set_uip_if_nonzero; constaddr(uip()) } | ||
| 227 | uop { @decode.outdata_a; @uart.tx } | ||
| 228 | end | ||
diff --git a/modules.rb b/modules.rb new file mode 100644 index 0000000..36d3b83 --- /dev/null +++ b/modules.rb | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | urom :alu, :op, :op_sel0, :op_sel1, :op_sel2, :outaddr, :outdata | ||
| 2 | urom :control, :halt, :set_uip_cond, :nocond, :outaddr, :outdata | ||
| 3 | urom :decode, :clear, :decode, :outaddr, :outaddr_sel0, :outaddr_sel1, :outdata, :outdata_sel0, :outdata_sel1 | ||
| 4 | urom :memory, :store, :outdata | ||
| 5 | urom :pc, :load, :increment, :outaddr | ||
| 6 | urom :rf, :store, :reset, :outdata | ||
| 7 | urom :tmp0, :load, :load_sel0, :outaddr, :outdata | ||
| 8 | urom :tmp1, :load, :load_sel0, :outaddr, :outdata | ||
| 9 | urom :uart, :tx, :rx, :outdata, :outdata_sel0 | ||
| 10 | |||
| 11 | urom_alias :alu, :and, :op | ||
| 12 | urom_alias :alu, :or, :op, :op_sel0 | ||
| 13 | urom_alias :alu, :xor, :op, :op_sel1 | ||
| 14 | urom_alias :alu, :add, :op, :op_sel0, :op_sel1 | ||
| 15 | urom_alias :alu, :sub, :op, :op_sel2 | ||
| 16 | urom_alias :alu, :cmp, :op, :op_sel0, :op_sel2 | ||
| 17 | urom_alias :alu, :lshift, :op, :op_sel1, :op_sel2 | ||
| 18 | urom_alias :alu, :rshift, :op, :op_sel0, :op_sel1, :op_sel2 | ||
| 19 | urom_alias :control, :set_uip, :set_uip_cond, :nocond | ||
| 20 | urom_alias :control, :set_uip_if_nonzero, :set_uip_cond | ||
| 21 | urom_alias :decode, :outaddr_a, :outaddr | ||
| 22 | urom_alias :decode, :outaddr_b, :outaddr, :outaddr_sel0 | ||
| 23 | urom_alias :decode, :outaddr_uip, :outaddr, :outaddr_sel1 | ||
| 24 | urom_alias :decode, :outdata_a, :outdata | ||
| 25 | urom_alias :decode, :outdata_b, :outdata, :outdata_sel0 | ||
| 26 | urom_alias :decode, :outdata_needmore, :outdata, :outdata_sel1 | ||
| 27 | urom_alias :tmp0, :loaddata, :load | ||
| 28 | urom_alias :tmp0, :loadaddr, :load, :load_sel0 | ||
| 29 | urom_alias :tmp1, :loaddata, :load | ||
| 30 | urom_alias :tmp1, :loadaddr, :load, :load_sel0 | ||
| 31 | urom_alias :uart, :outdata_txfull, :outdata | ||
| 32 | urom_alias :uart, :outdata_rxempty, :outdata, :outdata_sel0 | ||
diff --git a/sim/.gitignore b/sim/.gitignore new file mode 100644 index 0000000..dc3fae8 --- /dev/null +++ b/sim/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | /build | ||
| 2 | /verilator | ||
diff --git a/sim/Makefile b/sim/Makefile new file mode 100644 index 0000000..af9d22a --- /dev/null +++ b/sim/Makefile | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | run: build/Vtop | ||
| 2 | $< | ||
| 3 | .PHONY: run | ||
| 4 | |||
| 5 | build/Vtop: $(wildcard *.cpp *.sv) | ||
| 6 | @rm -rf verilator | ||
| 7 | @mkdir -p build verilator | ||
| 8 | verilator +1800-2017ext+sv -Wall -Wno-BLKSEQ -Wno-UNUSED -O3 -Ihdl --Mdir verilator --trace --cc --build -j --exe --top-module top $(wildcard *.cpp *.sv) | ||
| 9 | @ln -t build -f verilator/Vtop | ||
| 10 | @rm -rf verilator | ||
| 11 | |||
| 12 | .SECONDARY: | ||
diff --git a/sim/alu.sv b/sim/alu.sv new file mode 100644 index 0000000..407b083 --- /dev/null +++ b/sim/alu.sv | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | module alu | ||
| 2 | #( parameter UROM = "<no file specified>" | ||
| 3 | , parameter UIP_BITS = 15 | ||
| 4 | , parameter UROM_BITS = 8 | ||
| 5 | , parameter BUS_BITS = 16 | ||
| 6 | ) | ||
| 7 | ( input bit clk | ||
| 8 | , input bit reset | ||
| 9 | , input bit [UIP_BITS-1:0] uip | ||
| 10 | , inout bit [BUS_BITS-1:0] abus | ||
| 11 | , inout bit [BUS_BITS-1:0] dbus | ||
| 12 | ); | ||
| 13 | |||
| 14 | bit [BUS_BITS-1:0] x; | ||
| 15 | |||
| 16 | typedef enum | ||
| 17 | { OP | ||
| 18 | , OP_SEL0 | ||
| 19 | , OP_SEL1 | ||
| 20 | , OP_SEL2 | ||
| 21 | , OUTADDR | ||
| 22 | , OUTDATA | ||
| 23 | } CtrlBit; | ||
| 24 | |||
| 25 | bit [UROM_BITS-1:0] ctrl; | ||
| 26 | urom#(UROM, UIP_BITS, UROM_BITS) urom(uip, ctrl); | ||
| 27 | |||
| 28 | bit [2:0] sel; | ||
| 29 | assign sel = {ctrl[OP_SEL2], ctrl[OP_SEL1], ctrl[OP_SEL0]}; | ||
| 30 | |||
| 31 | bit [BUS_BITS-1:0] and_result; | ||
| 32 | bit [BUS_BITS-1:0] or_result; | ||
| 33 | bit [BUS_BITS-1:0] xor_result; | ||
| 34 | bit [BUS_BITS-1:0] add_result; | ||
| 35 | bit [BUS_BITS-1:0] sub_result; | ||
| 36 | bit [BUS_BITS-1:0] cmp_result; | ||
| 37 | bit [BUS_BITS-1:0] lshift_result; | ||
| 38 | bit [BUS_BITS-1:0] rshift_result; | ||
| 39 | |||
| 40 | assign and_result = abus & dbus; | ||
| 41 | assign or_result = abus | dbus; | ||
| 42 | assign xor_result = abus ^ dbus; | ||
| 43 | assign add_result = abus + dbus; | ||
| 44 | assign sub_result = abus - dbus; | ||
| 45 | assign cmp_result = {{(BUS_BITS-6){1'b0}}, | ||
| 46 | (abus != 0) & (dbus != 0), | ||
| 47 | (abus != 0) | (dbus != 0), | ||
| 48 | (abus != 0) ^ (dbus != 0), | ||
| 49 | abus > dbus, | ||
| 50 | abus == dbus, | ||
| 51 | abus < dbus}; | ||
| 52 | assign lshift_result = (dbus >= BUS_BITS) ? 0 : (abus << dbus); | ||
| 53 | assign rshift_result = (dbus >= BUS_BITS) ? 0 : (abus >> dbus); | ||
| 54 | |||
| 55 | bit [BUS_BITS-1:0] newx; | ||
| 56 | assign newx = | ||
| 57 | (sel == 0) ? and_result : | ||
| 58 | (sel == 1) ? or_result : | ||
| 59 | (sel == 2) ? xor_result : | ||
| 60 | (sel == 3) ? add_result : | ||
| 61 | (sel == 4) ? sub_result : | ||
| 62 | (sel == 5) ? cmp_result : | ||
| 63 | (sel == 6) ? lshift_result : | ||
| 64 | (sel == 7) ? rshift_result : | ||
| 65 | {(BUS_BITS){1'bX}}; | ||
| 66 | |||
| 67 | assign abus = ctrl[OUTADDR] ? x : {(BUS_BITS){1'bZ}}; | ||
| 68 | assign dbus = ctrl[OUTDATA] ? x : {(BUS_BITS){1'bZ}}; | ||
| 69 | |||
| 70 | always @(posedge clk) begin | ||
| 71 | if (ctrl[OP]) begin | ||
| 72 | x <= newx; | ||
| 73 | end | ||
| 74 | end | ||
| 75 | |||
| 76 | endmodule | ||
diff --git a/sim/control.sv b/sim/control.sv new file mode 100644 index 0000000..7808f61 --- /dev/null +++ b/sim/control.sv | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | module control | ||
| 2 | #( parameter UROM = "<no file specified>" | ||
| 3 | , parameter UIP_BITS = 15 | ||
| 4 | , parameter UROM_BITS = 8 | ||
| 5 | , parameter BUS_BITS = 16 | ||
| 6 | , parameter CONST_0 = "<no file specified>" | ||
| 7 | , parameter CONST_1 = "<no file specified>" | ||
| 8 | , parameter RESET = ~0 | ||
| 9 | ) | ||
| 10 | ( input bit clk | ||
| 11 | , input bit reset | ||
| 12 | , output bit [UIP_BITS-1:0] uip | ||
| 13 | , inout bit [BUS_BITS-1:0] abus | ||
| 14 | , inout bit [BUS_BITS-1:0] dbus | ||
| 15 | ); | ||
| 16 | |||
| 17 | typedef enum | ||
| 18 | { HALT | ||
| 19 | , SET_UIP_COND | ||
| 20 | , NOCOND | ||
| 21 | , OUTADDR | ||
| 22 | , OUTDATA | ||
| 23 | } CtrlBit; | ||
| 24 | |||
| 25 | bit [UROM_BITS-1:0] ctrl; | ||
| 26 | urom#(UROM, UIP_BITS, UROM_BITS) urom(uip, ctrl); | ||
| 27 | |||
| 28 | bit [UROM_BITS*2-1:0] constant; | ||
| 29 | urom#(CONST_0, UIP_BITS, UROM_BITS) const_0(uip, constant[1*UROM_BITS-1:0*UROM_BITS]); | ||
| 30 | urom#(CONST_1, UIP_BITS, UROM_BITS) const_1(uip, constant[2*UROM_BITS-1:1*UROM_BITS]); | ||
| 31 | |||
| 32 | assign abus = ctrl[OUTADDR] ? constant : {(BUS_BITS){1'bZ}}; | ||
| 33 | assign dbus = ctrl[OUTDATA] ? constant : {(BUS_BITS){1'bZ}}; | ||
| 34 | |||
| 35 | bit cond; | ||
| 36 | assign cond = (dbus != 0) || ctrl[NOCOND]; | ||
| 37 | |||
| 38 | always @(posedge clk) begin | ||
| 39 | if (reset) begin | ||
| 40 | uip <= RESET; | ||
| 41 | end else begin | ||
| 42 | if (! ctrl[HALT]) begin | ||
| 43 | if (ctrl[SET_UIP_COND] && cond) begin | ||
| 44 | uip <= abus[UIP_BITS-1:0]; | ||
| 45 | end else begin | ||
| 46 | uip <= uip + 1; | ||
| 47 | end | ||
| 48 | end else begin | ||
| 49 | $finish; | ||
| 50 | end | ||
| 51 | end | ||
| 52 | end | ||
| 53 | |||
| 54 | endmodule | ||
diff --git a/sim/decode.sv b/sim/decode.sv new file mode 100644 index 0000000..5c9a9c9 --- /dev/null +++ b/sim/decode.sv | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | module decode | ||
| 2 | #( parameter UROM = "<no file specified>" | ||
| 3 | , parameter UIP_BITS = 15 | ||
| 4 | , parameter UROM_BITS = 8 | ||
| 5 | , parameter BUS_BITS = 16 | ||
| 6 | , parameter OPCODE_BITS = 8 | ||
| 7 | ) | ||
| 8 | ( input bit clk | ||
| 9 | , input bit reset | ||
| 10 | , input bit [UIP_BITS-1:0] uip | ||
| 11 | , inout bit [BUS_BITS-1:0] abus | ||
| 12 | , inout bit [BUS_BITS-1:0] dbus | ||
| 13 | ); | ||
| 14 | |||
| 15 | bit [OPCODE_BITS-1:0] opcode; | ||
| 16 | bit [BUS_BITS-1:0] a; | ||
| 17 | bit [BUS_BITS-1:0] b; | ||
| 18 | bit needmore; | ||
| 19 | |||
| 20 | bit [UIP_BITS-1:0] new_uip; | ||
| 21 | assign new_uip = {opcode, {(UIP_BITS-OPCODE_BITS){1'b0}}}; | ||
| 22 | |||
| 23 | typedef enum | ||
| 24 | { CLEAR | ||
| 25 | , DECODE | ||
| 26 | , OUTADDR | ||
| 27 | , OUTADDR_SEL0 | ||
| 28 | , OUTADDR_SEL1 | ||
| 29 | , OUTDATA | ||
| 30 | , OUTDATA_SEL0 | ||
| 31 | , OUTDATA_SEL1 | ||
| 32 | } CtrlBit; | ||
| 33 | |||
| 34 | bit [UROM_BITS-1:0] ctrl; | ||
| 35 | urom#(UROM, UIP_BITS, UROM_BITS) urom(uip, ctrl); | ||
| 36 | |||
| 37 | bit [1:0] asel; | ||
| 38 | assign asel = {ctrl[OUTADDR_SEL1], ctrl[OUTADDR_SEL0]}; | ||
| 39 | |||
| 40 | bit [1:0] dsel; | ||
| 41 | assign dsel = {ctrl[OUTDATA_SEL1], ctrl[OUTDATA_SEL0]}; | ||
| 42 | |||
| 43 | bit [BUS_BITS-1:0] aout; | ||
| 44 | assign aout = | ||
| 45 | (asel == 0) ? a : | ||
| 46 | (asel == 1) ? b : | ||
| 47 | (asel == 2) ? {{(BUS_BITS-UIP_BITS){1'b0}}, new_uip} : | ||
| 48 | {(BUS_BITS){1'bX}}; | ||
| 49 | |||
| 50 | assign abus = ctrl[OUTADDR] ? aout : {(BUS_BITS){1'bZ}}; | ||
| 51 | |||
| 52 | bit [BUS_BITS-1:0] dout; | ||
| 53 | assign dout = | ||
| 54 | (dsel == 0) ? a : | ||
| 55 | (dsel == 1) ? b : | ||
| 56 | (dsel == 2) ? {{(BUS_BITS-1){1'b0}}, needmore} : | ||
| 57 | {(BUS_BITS){1'bX}}; | ||
| 58 | |||
| 59 | assign dbus = ctrl[OUTDATA] ? dout : {(BUS_BITS){1'bZ}}; | ||
| 60 | |||
| 61 | always @(posedge clk) begin | ||
| 62 | if (reset || ctrl[CLEAR]) begin | ||
| 63 | opcode <= 0; | ||
| 64 | a <= 0; | ||
| 65 | b <= 0; | ||
| 66 | needmore <= 1; | ||
| 67 | end else if (ctrl[DECODE]) begin | ||
| 68 | automatic bit m = dbus[7]; | ||
| 69 | automatic bit i = dbus[6]; | ||
| 70 | automatic bit [1:0] field = dbus[5:4]; | ||
| 71 | automatic bit [3:0] shift = dbus[3:0]; | ||
| 72 | needmore <= m; | ||
| 73 | if (field == 0) begin | ||
| 74 | opcode <= {i ? ~opcode[OPCODE_BITS-1-4:0] : opcode[OPCODE_BITS-1-4:0], shift}; | ||
| 75 | end else if (field == 2) begin | ||
| 76 | a <= {i ? ~a[BUS_BITS-1-4:0] : a[BUS_BITS-1-4:0], shift}; | ||
| 77 | end else if (field == 3) begin | ||
| 78 | b <= {i ? ~b[BUS_BITS-1-4:0] : b[BUS_BITS-1-4:0], shift}; | ||
| 79 | end | ||
| 80 | end | ||
| 81 | end | ||
| 82 | endmodule | ||
diff --git a/sim/main.cpp b/sim/main.cpp new file mode 100644 index 0000000..ac4befa --- /dev/null +++ b/sim/main.cpp | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | #include <cstdint> | ||
| 2 | #include <iostream> | ||
| 3 | #include <verilated.h> | ||
| 4 | #include <verilated_vcd_c.h> | ||
| 5 | |||
| 6 | #include "Vtop.h" | ||
| 7 | |||
| 8 | std::uint64_t phases = 0; | ||
| 9 | |||
| 10 | double sc_time_stamp() | ||
| 11 | { | ||
| 12 | return (double)phases / 2; | ||
| 13 | } | ||
| 14 | |||
| 15 | int main(int argc, const char *argv[]) | ||
| 16 | { | ||
| 17 | Verilated::commandArgs(argc, argv); | ||
| 18 | |||
| 19 | Verilated::traceEverOn(true); | ||
| 20 | VerilatedVcdC vcd; | ||
| 21 | |||
| 22 | Vtop top; | ||
| 23 | top.trace(&vcd, 100 /* levels of hierarchy */); | ||
| 24 | |||
| 25 | vcd.set_time_unit("ns"); | ||
| 26 | vcd.set_time_resolution("ns"); | ||
| 27 | vcd.open("build/out.vcd"); | ||
| 28 | |||
| 29 | std::cout << "*** RESET SEQUENCE ***\n"; | ||
| 30 | |||
| 31 | top.clk = 0; | ||
| 32 | top.reset = 1; | ||
| 33 | top.eval(); | ||
| 34 | vcd.dump(++phases); | ||
| 35 | |||
| 36 | top.clk = 1; | ||
| 37 | top.eval(); | ||
| 38 | vcd.dump(++phases); | ||
| 39 | |||
| 40 | top.clk = 0; | ||
| 41 | top.reset = 0; | ||
| 42 | top.eval(); | ||
| 43 | vcd.dump(++phases); | ||
| 44 | |||
| 45 | std::cout << "*** MAIN LOOP ***\n"; | ||
| 46 | |||
| 47 | for (unsigned int i = 0; i < 10000 && !Verilated::gotFinish(); ++i) { | ||
| 48 | top.clk = 1; | ||
| 49 | top.eval(); | ||
| 50 | vcd.dump(++phases); | ||
| 51 | top.clk = 0; | ||
| 52 | top.eval(); | ||
| 53 | vcd.dump(++phases); | ||
| 54 | } | ||
| 55 | |||
| 56 | std::cout << "\n"; | ||
| 57 | |||
| 58 | vcd.close(); | ||
| 59 | |||
| 60 | return 0; | ||
| 61 | } | ||
diff --git a/sim/memory.sv b/sim/memory.sv new file mode 100644 index 0000000..0eb1233 --- /dev/null +++ b/sim/memory.sv | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | module memory | ||
| 2 | #( parameter UROM = "<no file specified>" | ||
| 3 | , parameter UIP_BITS = 15 | ||
| 4 | , parameter UROM_BITS = 8 | ||
| 5 | , parameter BUS_BITS = 16 | ||
| 6 | , parameter IMAGE = "<no file specified>" | ||
| 7 | , parameter BYTE_BITS = 8 | ||
| 8 | ) | ||
| 9 | ( input bit clk | ||
| 10 | , input bit reset | ||
| 11 | , input bit [UIP_BITS-1:0] uip | ||
| 12 | , inout bit [BUS_BITS-1:0] abus | ||
| 13 | , inout bit [BUS_BITS-1:0] dbus | ||
| 14 | ); | ||
| 15 | |||
| 16 | bit [BYTE_BITS-1:0] storage [0:(1<<BUS_BITS)-1]; | ||
| 17 | initial $readmemh(IMAGE, storage); | ||
| 18 | |||
| 19 | typedef enum | ||
| 20 | { STORE | ||
| 21 | , OUTDATA | ||
| 22 | } CtrlBit; | ||
| 23 | |||
| 24 | bit [UROM_BITS-1:0] ctrl; | ||
| 25 | urom#(UROM, UIP_BITS, UROM_BITS) urom(uip, ctrl); | ||
| 26 | |||
| 27 | assign dbus = ctrl[OUTDATA] ? {{(BUS_BITS-BYTE_BITS){1'b0}}, storage[abus]} : {(BUS_BITS){1'bZ}}; | ||
| 28 | |||
| 29 | always @(posedge clk) begin | ||
| 30 | if (ctrl[STORE]) begin | ||
| 31 | storage[abus] <= dbus[BYTE_BITS-1:0]; | ||
| 32 | end | ||
| 33 | end | ||
| 34 | |||
| 35 | endmodule | ||
diff --git a/sim/pc.sv b/sim/pc.sv new file mode 100644 index 0000000..1a3aaee --- /dev/null +++ b/sim/pc.sv | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | module pc | ||
| 2 | #( parameter UROM = "<no file specified>" | ||
| 3 | , parameter UIP_BITS = 15 | ||
| 4 | , parameter UROM_BITS = 8 | ||
| 5 | , parameter BUS_BITS = 16 | ||
| 6 | , parameter RESET = 0 | ||
| 7 | ) | ||
| 8 | ( input bit clk | ||
| 9 | , input bit reset | ||
| 10 | , input bit [UIP_BITS-1:0] uip | ||
| 11 | , inout bit [BUS_BITS-1:0] abus | ||
| 12 | , inout bit [BUS_BITS-1:0] dbus | ||
| 13 | ); | ||
| 14 | |||
| 15 | bit [BUS_BITS-1:0] addr; | ||
| 16 | |||
| 17 | typedef enum | ||
| 18 | { LOAD | ||
| 19 | , INCREMENT | ||
| 20 | , OUTADDR | ||
| 21 | } CtrlBit; | ||
| 22 | |||
| 23 | bit [UROM_BITS-1:0] ctrl; | ||
| 24 | urom#(UROM, UIP_BITS, UROM_BITS) urom(uip, ctrl); | ||
| 25 | |||
| 26 | assign abus = ctrl[OUTADDR] ? addr : {(BUS_BITS){1'bZ}}; | ||
| 27 | |||
| 28 | always @(posedge clk) begin | ||
| 29 | if (reset) begin | ||
| 30 | addr <= RESET; | ||
| 31 | end else begin | ||
| 32 | addr <= (ctrl[LOAD] ? abus : addr) + (ctrl[INCREMENT] ? 1 : 0); | ||
| 33 | end | ||
| 34 | end | ||
| 35 | |||
| 36 | endmodule | ||
diff --git a/sim/rf.sv b/sim/rf.sv new file mode 100644 index 0000000..37502ce --- /dev/null +++ b/sim/rf.sv | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | module rf | ||
| 2 | #( parameter UROM = "<no file specified>" | ||
| 3 | , parameter UIP_BITS = 15 | ||
| 4 | , parameter UROM_BITS = 8 | ||
| 5 | , parameter BUS_BITS = 16 | ||
| 6 | , parameter NAME_BITS = 3 | ||
| 7 | ) | ||
| 8 | ( input bit clk | ||
| 9 | , input bit reset | ||
| 10 | , input bit [UIP_BITS-1:0] uip | ||
| 11 | , inout bit [BUS_BITS-1:0] abus | ||
| 12 | , inout bit [BUS_BITS-1:0] dbus | ||
| 13 | ); | ||
| 14 | |||
| 15 | bit [BUS_BITS-1:0] storage [0:(1<<NAME_BITS)-1]; | ||
| 16 | |||
| 17 | typedef enum | ||
| 18 | { STORE | ||
| 19 | , RESET | ||
| 20 | , OUTDATA | ||
| 21 | } CtrlBit; | ||
| 22 | |||
| 23 | bit [UROM_BITS-1:0] ctrl; | ||
| 24 | urom#(UROM, UIP_BITS, UROM_BITS) urom(uip, ctrl); | ||
| 25 | |||
| 26 | assign dbus = ctrl[OUTDATA] ? storage[abus[NAME_BITS-1:0]] : {(BUS_BITS){1'bZ}}; | ||
| 27 | |||
| 28 | always @(posedge clk) begin | ||
| 29 | if (reset || ctrl[RESET]) begin | ||
| 30 | for (int i = 0; i < (1 << NAME_BITS); ++i) | ||
| 31 | storage[i] <= 0; | ||
| 32 | end else begin | ||
| 33 | if (ctrl[STORE]) begin | ||
| 34 | storage[abus[NAME_BITS-1:0]] <= dbus; | ||
| 35 | end | ||
| 36 | end | ||
| 37 | end | ||
| 38 | |||
| 39 | endmodule | ||
diff --git a/sim/tmp.sv b/sim/tmp.sv new file mode 100644 index 0000000..61e35f7 --- /dev/null +++ b/sim/tmp.sv | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | module tmp | ||
| 2 | #( parameter UROM = "<no file specified>" | ||
| 3 | , parameter UIP_BITS = 15 | ||
| 4 | , parameter UROM_BITS = 8 | ||
| 5 | , parameter BUS_BITS = 16 | ||
| 6 | ) | ||
| 7 | ( input bit clk | ||
| 8 | , input bit reset | ||
| 9 | , input bit [UIP_BITS-1:0] uip | ||
| 10 | , inout bit [BUS_BITS-1:0] abus | ||
| 11 | , inout bit [BUS_BITS-1:0] dbus | ||
| 12 | ); | ||
| 13 | |||
| 14 | bit [BUS_BITS-1:0] x; | ||
| 15 | |||
| 16 | typedef enum | ||
| 17 | { LOAD | ||
| 18 | , LOAD_SEL0 | ||
| 19 | , OUTADDR | ||
| 20 | , OUTDATA | ||
| 21 | } CtrlBit; | ||
| 22 | |||
| 23 | bit [UROM_BITS-1:0] ctrl; | ||
| 24 | urom#(UROM, UIP_BITS, UROM_BITS) urom(uip, ctrl); | ||
| 25 | |||
| 26 | bit [0:0] sel; | ||
| 27 | assign sel = {ctrl[LOAD_SEL0]}; | ||
| 28 | |||
| 29 | assign abus = ctrl[OUTADDR] ? x : {(BUS_BITS){1'bZ}}; | ||
| 30 | assign dbus = ctrl[OUTDATA] ? x : {(BUS_BITS){1'bZ}}; | ||
| 31 | |||
| 32 | always @(posedge clk) begin | ||
| 33 | if (ctrl[LOAD]) begin | ||
| 34 | x <= | ||
| 35 | (sel == 0) ? dbus : | ||
| 36 | (sel == 1) ? abus : | ||
| 37 | {(BUS_BITS){1'bX}}; | ||
| 38 | end | ||
| 39 | end | ||
| 40 | |||
| 41 | endmodule | ||
diff --git a/sim/top.sv b/sim/top.sv new file mode 100644 index 0000000..6773739 --- /dev/null +++ b/sim/top.sv | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | module top | ||
| 2 | #( parameter UIP_BITS = 15 | ||
| 3 | , parameter UROM_BITS = 8 | ||
| 4 | , parameter BUS_BITS = 16 | ||
| 5 | , parameter MEM_BITS = 8 | ||
| 6 | ) | ||
| 7 | ( input bit clk // verilator public | ||
| 8 | , input bit reset // verilator public | ||
| 9 | ); | ||
| 10 | |||
| 11 | bit [UIP_BITS-1:0] uip; | ||
| 12 | bit [BUS_BITS-1:0] abus; | ||
| 13 | bit [BUS_BITS-1:0] dbus; | ||
| 14 | |||
| 15 | alu #("../out/alu.bin", UIP_BITS, UROM_BITS, BUS_BITS) alu(clk, reset, uip, abus, dbus); | ||
| 16 | control #("../out/control.bin", UIP_BITS, UROM_BITS, BUS_BITS, "../out/consts.0.bin", "../out/consts.1.bin", 'h7ff8) control(clk, reset, uip, abus, dbus); | ||
| 17 | decode #("../out/decode.bin", UIP_BITS, UROM_BITS, BUS_BITS, 12) decode(clk, reset, uip, abus, dbus); | ||
| 18 | memory #("../out/memory.bin", UIP_BITS, UROM_BITS, BUS_BITS, "../out/image.hex", MEM_BITS) memory(clk, reset, uip, abus, dbus); | ||
| 19 | pc #("../out/pc.bin", UIP_BITS, UROM_BITS, BUS_BITS, 0) pc(clk, reset, uip, abus, dbus); | ||
| 20 | rf #("../out/rf.bin", UIP_BITS, UROM_BITS, BUS_BITS, 3) rf(clk, reset, uip, abus, dbus); | ||
| 21 | tmp #("../out/tmp0.bin", UIP_BITS, UROM_BITS, BUS_BITS) tmp0(clk, reset, uip, abus, dbus); | ||
| 22 | tmp #("../out/tmp1.bin", UIP_BITS, UROM_BITS, BUS_BITS) tmp1(clk, reset, uip, abus, dbus); | ||
| 23 | uart #("../out/uart.bin", UIP_BITS, UROM_BITS, BUS_BITS) uart(clk, reset, uip, abus, dbus); | ||
| 24 | |||
| 25 | //always @(negedge clk) $display("pc=%x uip=%x abus=%x dbus=%x tmp0=%x tmp1=%x alu=%x regs=%x:%x:%x:...:%x", pc.addr, uip, abus, dbus, tmp0.x, tmp1.x, alu.x, rf.storage[0], rf.storage[1], rf.storage[2], rf.storage[7]); | ||
| 26 | |||
| 27 | endmodule | ||
diff --git a/sim/uart.sv b/sim/uart.sv new file mode 100644 index 0000000..3b434da --- /dev/null +++ b/sim/uart.sv | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | module uart | ||
| 2 | #( parameter UROM = "<no file specified>" | ||
| 3 | , parameter UIP_BITS = 15 | ||
| 4 | , parameter UROM_BITS = 8 | ||
| 5 | , parameter BUS_BITS = 16 | ||
| 6 | ) | ||
| 7 | ( input bit clk | ||
| 8 | , input bit reset | ||
| 9 | , input bit [UIP_BITS-1:0] uip | ||
| 10 | , inout bit [BUS_BITS-1:0] abus | ||
| 11 | , inout bit [BUS_BITS-1:0] dbus | ||
| 12 | ); | ||
| 13 | |||
| 14 | bit txfull; | ||
| 15 | bit rxempty; | ||
| 16 | |||
| 17 | assign txfull = 0; | ||
| 18 | assign rxempty = 0; | ||
| 19 | |||
| 20 | typedef enum | ||
| 21 | { TX | ||
| 22 | , RX | ||
| 23 | , OUTDATA | ||
| 24 | , OUTDATA_SEL0 | ||
| 25 | } CtrlBit; | ||
| 26 | |||
| 27 | bit [UROM_BITS-1:0] ctrl; | ||
| 28 | urom#(UROM, UIP_BITS, UROM_BITS) urom(uip, ctrl); | ||
| 29 | |||
| 30 | bit [0:0] sel; | ||
| 31 | assign sel = {ctrl[OUTDATA_SEL0]}; | ||
| 32 | |||
| 33 | bit [BUS_BITS-1:0] dout; | ||
| 34 | assign dout = | ||
| 35 | (ctrl[RX]) ? {(BUS_BITS){1'b1}} : | ||
| 36 | (sel == 0) ? {{(BUS_BITS-1){1'b0}}, txfull} : | ||
| 37 | (sel == 1) ? {{(BUS_BITS-1){1'b0}}, rxempty} : | ||
| 38 | {(BUS_BITS){1'bX}}; | ||
| 39 | |||
| 40 | assign dbus = ctrl[OUTDATA] ? dout : {(BUS_BITS){1'bZ}}; | ||
| 41 | |||
| 42 | always @(posedge clk) begin | ||
| 43 | if (ctrl[TX]) | ||
| 44 | $display("tx %x", dbus[7:0]); | ||
| 45 | end | ||
| 46 | |||
| 47 | endmodule | ||
diff --git a/sim/urom.sv b/sim/urom.sv new file mode 100644 index 0000000..e88f6d7 --- /dev/null +++ b/sim/urom.sv | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | module urom | ||
| 2 | #( parameter UROM = "<no file specified>" | ||
| 3 | , parameter ADDR_BITS = 15 | ||
| 4 | , parameter DATA_BITS = 8 | ||
| 5 | ) | ||
| 6 | ( input bit [ADDR_BITS-1:0] addr | ||
| 7 | , output bit [DATA_BITS-1:0] data | ||
| 8 | ); | ||
| 9 | |||
| 10 | bit [DATA_BITS-1:0] storage [0:(1<<ADDR_BITS)-1]; | ||
| 11 | initial begin | ||
| 12 | automatic int fh; | ||
| 13 | fh = $fopen(UROM, "rb"); | ||
| 14 | $fread(storage, fh); | ||
| 15 | end | ||
| 16 | |||
| 17 | assign data = storage[addr]; | ||
| 18 | |||
| 19 | endmodule | ||
| @@ -0,0 +1,174 @@ | |||
| 1 | #!/usr/bin/ruby -w | ||
| 2 | |||
| 3 | OPCODE_BITS = 12 | ||
| 4 | UROM_BITS = 15 | ||
| 5 | UIP_BITS_PER_OPCODE = UROM_BITS - OPCODE_BITS | ||
| 6 | |||
| 7 | $stdout.write(<<END) | ||
| 8 | OPCODE_BITS = #{OPCODE_BITS} | ||
| 9 | UROM_BITS = #{UROM_BITS} | ||
| 10 | UIP_BITS_PER_OPCODE = #{UIP_BITS_PER_OPCODE} (max #{(1<<UIP_BITS_PER_OPCODE)-1} uops (+1 eom) per inst) | ||
| 11 | |||
| 12 | END | ||
| 13 | |||
| 14 | $uroms = {consts: [0] * (1 << UROM_BITS)} | ||
| 15 | $urom_sizes = {consts: 16} | ||
| 16 | $insts = {} | ||
| 17 | |||
| 18 | $this_inst = nil | ||
| 19 | $this_uop = nil | ||
| 20 | |||
| 21 | $next_opcode = 0 | ||
| 22 | $next_vector = (1 << OPCODE_BITS) - 1 | ||
| 23 | |||
| 24 | system("rm -rf out; mkdir -p out") | ||
| 25 | |||
| 26 | def urom(name, *bits) | ||
| 27 | throw "non-unique bit names" unless bits == bits.uniq | ||
| 28 | throw "µrom with this name already defined" if $uroms.member?(name) | ||
| 29 | $uroms[name] = [0] * (1 << UROM_BITS) | ||
| 30 | $urom_sizes[name] = bits.size | ||
| 31 | proxy = Object.new | ||
| 32 | $stdout.write("\n\t#{name}:\n") | ||
| 33 | bitmap = {} | ||
| 34 | bits.each_with_index do | bit, idx | | ||
| 35 | if bits.size > 8 | ||
| 36 | $stdout.write("\t\t#{idx >> 3}.#{idx & 0x7}\t#{bit}\n") | ||
| 37 | else | ||
| 38 | $stdout.write("\t\t#{idx}\t#{bit}\n") | ||
| 39 | end | ||
| 40 | bitmap[bit] = 1 << idx | ||
| 41 | proxy.define_singleton_method(bit) do | ||
| 42 | if $this_uop | ||
| 43 | $this_uop[name] |= 1 << idx | ||
| 44 | else | ||
| 45 | uop do | ||
| 46 | $this_uop[name] |= 1 << idx | ||
| 47 | end | ||
| 48 | end | ||
| 49 | end | ||
| 50 | end | ||
| 51 | proxy.instance_variable_set(:@bitmap, bitmap) | ||
| 52 | instance_variable_set("@#{name}", proxy) | ||
| 53 | end | ||
| 54 | |||
| 55 | def urom_alias(urom, name, *bits) | ||
| 56 | merged = 0 | ||
| 57 | proxy = instance_variable_get("@#{urom}") | ||
| 58 | bitmap = proxy.instance_variable_get(:@bitmap) | ||
| 59 | throw "Name already in use" if bitmap.member?(name) | ||
| 60 | bits.each do | bit | | ||
| 61 | merged |= bitmap[bit] | ||
| 62 | end | ||
| 63 | bitmap[name] = merged | ||
| 64 | proxy.define_singleton_method(name) do | ||
| 65 | if $this_uop | ||
| 66 | $this_uop[urom] |= merged | ||
| 67 | else | ||
| 68 | uop { $this_uop[urom] |= merged } | ||
| 69 | end | ||
| 70 | end | ||
| 71 | proxy.instance_variable_set(:@bitmap, bitmap) | ||
| 72 | end | ||
| 73 | |||
| 74 | def inst(pattern, vector=false, &code) | ||
| 75 | throw "nested instruction" if $this_inst | ||
| 76 | throw "instruction with this pattern already defined" if $insts.member?(pattern) | ||
| 77 | throw "too many opcodes" if $next_opcode > $next_vector | ||
| 78 | $this_opnum = vector ? $next_vector : $next_opcode | ||
| 79 | $stdout.write("\t#{$this_opnum.to_s(16)}\t#{($this_opnum << UIP_BITS_PER_OPCODE).to_s(16)}\t#{pattern}\n") | ||
| 80 | $this_inst = [] | ||
| 81 | code.call | ||
| 82 | if not vector | ||
| 83 | $eom ||= uip() | ||
| 84 | uop { @decode.clear; @control.set_uip; constaddr($insts["!bom"] << UIP_BITS_PER_OPCODE) } | ||
| 85 | end | ||
| 86 | $this_inst.each_with_index do | uop, idx | | ||
| 87 | uip = ($this_opnum << UIP_BITS_PER_OPCODE) + idx | ||
| 88 | uop.each do | urom, entry | | ||
| 89 | $uroms[urom][uip] = entry | ||
| 90 | end | ||
| 91 | end | ||
| 92 | $insts[pattern] = $this_opnum | ||
| 93 | $this_opnum = nil | ||
| 94 | $this_inst = nil | ||
| 95 | if vector | ||
| 96 | $next_vector -= 1 | ||
| 97 | else | ||
| 98 | $next_opcode += 1 | ||
| 99 | end | ||
| 100 | end | ||
| 101 | |||
| 102 | def vector(name, &code) | ||
| 103 | inst("!#{name}", true, &code) | ||
| 104 | end | ||
| 105 | |||
| 106 | def uop(&code) | ||
| 107 | throw "not in instruction" unless $this_inst | ||
| 108 | throw "nested uop" if $this_uop | ||
| 109 | throw "too many uops" if $this_inst.size >= (1 << UIP_BITS_PER_OPCODE) | ||
| 110 | $this_uop = {} | ||
| 111 | $uroms.each_key do | urom | | ||
| 112 | $this_uop[urom] = 0 | ||
| 113 | end | ||
| 114 | code.call | ||
| 115 | $this_inst << $this_uop | ||
| 116 | $this_uop = nil | ||
| 117 | end | ||
| 118 | |||
| 119 | def const(value) | ||
| 120 | throw "Value out of range" if value < 0 or value > 0xffff | ||
| 121 | if $this_uop | ||
| 122 | $this_uop[:consts] = value | ||
| 123 | else | ||
| 124 | uop do | ||
| 125 | $this_uop[:consts] = value | ||
| 126 | end | ||
| 127 | end | ||
| 128 | end | ||
| 129 | |||
| 130 | def constaddr(value) | ||
| 131 | const(value) | ||
| 132 | @control.outaddr | ||
| 133 | end | ||
| 134 | |||
| 135 | def constdata(value) | ||
| 136 | const(value) | ||
| 137 | @control.outdata | ||
| 138 | end | ||
| 139 | |||
| 140 | def uip() | ||
| 141 | throw "not in instruction" unless $this_inst | ||
| 142 | return ($this_opnum << UIP_BITS_PER_OPCODE) + $this_inst.size | ||
| 143 | end | ||
| 144 | |||
| 145 | $stdout.write("Control wires:\n") | ||
| 146 | load "modules.rb" | ||
| 147 | |||
| 148 | $stdout.write("\nOpcodes:\n") | ||
| 149 | load "vectors.rb" | ||
| 150 | load "insts.rb" | ||
| 151 | |||
| 152 | $stdout.write("\nWriting µrom contents:\n") | ||
| 153 | $uroms.each do | urom, data | | ||
| 154 | $stdout.write("\t#{urom}...") | ||
| 155 | $stdout.flush | ||
| 156 | if $urom_sizes[urom] > 8 | ||
| 157 | 0.upto(($urom_sizes[urom] / 8.0).ceil - 1) do | bank | | ||
| 158 | $stdout.write(" #{bank}") | ||
| 159 | $stdout.flush | ||
| 160 | File.open("out/#{urom}.#{bank}.bin", "wb") do | fh | | ||
| 161 | data.each do | word | | ||
| 162 | fh.write(((word >> (8 * bank)) & 0xff).chr) | ||
| 163 | end | ||
| 164 | end | ||
| 165 | end | ||
| 166 | else | ||
| 167 | File.open("out/#{urom}.bin", "wb") do | fh | | ||
| 168 | data.each do | byte | | ||
| 169 | fh.write(byte.chr) | ||
| 170 | end | ||
| 171 | end | ||
| 172 | end | ||
| 173 | $stdout.write("\n") | ||
| 174 | end | ||
diff --git a/vectors.rb b/vectors.rb new file mode 100644 index 0000000..62b58d5 --- /dev/null +++ b/vectors.rb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | vector :bom do | ||
| 2 | $start_of_bom = uip() | ||
| 3 | decode_loop = uip() | ||
| 4 | uop { @pc.increment; @pc.outaddr; @memory.outdata; @decode.decode } | ||
| 5 | uop { @decode.outdata_needmore; @control.set_uip_if_nonzero; constaddr(decode_loop) } | ||
| 6 | uop { @decode.outaddr_uip; @control.set_uip } | ||
| 7 | end | ||
