Import stack-oriented CPU design from Mercurial repository.

This commit is contained in:
Jesse D. McDonald 2013-02-09 12:46:27 -06:00
commit 0da3692eb8
30 changed files with 3580 additions and 0 deletions

35
.gitignore vendored Normal file
View File

@ -0,0 +1,35 @@
/.lastasm
/program.data
/program.dis
/program.sym
tmp/
/*.lso
/*.lst
/*.bgn
/*.bit
/*.bld
/*.drc
/*.map
/*.mrp
/*.ncd
/*.ngc
/*.ngd
/*.ngm
/*.ngr
/*.pad
/*.par
/*.pcf
/*.ptwx
/*.srp
/*.unroutes
/*.xwbt
/*.xpi
/*.xrpt
/*_pad.csv
/*_pad.txt
/*_vhdl.prj
/*_summary.xml
/*_usage.xml
/_xmsgs/
/xst/
/xlnx_auto_0_xdb/

28
Makefile Normal file
View File

@ -0,0 +1,28 @@
DESIGN = toplevel
TOPLEVEL = TopLevel
ASM = asm/monitor.txt
NGDBUILD_FLAGS ?= -aul
MAP_FLAGS ?= -timing -ol high -xe n -detail
PAR_FLAGS ?= -ol high -xe n
ifneq ($(shell cat .lastasm 2>/dev/null),$(ASM))
# ASM changed, rebuild program.data even if up to date
.PHONY: program.data program.dis program.sym
endif
OTHER_FILES = program.data
-include $(XILINX)/Makefile.common
program.sym program.dis: program.data
program.data: $(ASM) $(shell ./assemble.rb --depends $(ASM)) assemble.rb
./assemble.rb --code=program.dis --syms=program.sym \
--origin=0 --format=hex -- $< > $@
echo $(ASM) > .lastasm
clean: clean-local
.PHONY: clean-local
clean-local:
-rm -f program.data program.dis program.sym .lastasm

79
arbiter.v Normal file
View File

@ -0,0 +1,79 @@
// Simple 2:1 alternating (round-robin) arbiter.
// Switches master on ACK from slave unless selected master asserts LOCK.
// Data from slave is sent to both masters. Address and data bus widths
// are configurable, as is granularity. Default is 32-bit addresses and
// data with byte granularity.
module WB_ARB_2M (CLK_I, RST_I, SELECT_O,
M0_ADR_I, M0_DAT_I, M0_DAT_O, M0_LOCK_I, M0_CYC_I, M0_STB_I,
M0_WE_I, M0_SEL_I, M0_ACK_O, M0_ERR_O, M0_RTY_O,
M1_ADR_I, M1_DAT_I, M1_DAT_O, M1_LOCK_I, M1_CYC_I, M1_STB_I,
M1_WE_I, M1_SEL_I, M1_ACK_O, M1_ERR_O, M1_RTY_O,
S_ADR_O, S_DAT_O, S_DAT_I, S_LOCK_O, S_CYC_O, S_STB_O,
S_WE_O, S_SEL_O, S_ACK_I, S_ERR_I, S_RTY_I);
parameter ADDR_WIDTH = 30;
parameter DATA_WIDTH = 32;
parameter SEL_WIDTH = 4;
input CLK_I, RST_I;
output SELECT_O;
input [ADDR_WIDTH-1:0] M0_ADR_I, M1_ADR_I;
input [DATA_WIDTH-1:0] M0_DAT_I, M1_DAT_I;
output [DATA_WIDTH-1:0] M0_DAT_O, M1_DAT_O;
input M0_LOCK_I, M1_LOCK_I;
input M0_CYC_I, M1_CYC_I;
input M0_STB_I, M1_STB_I;
input M0_WE_I, M1_WE_I;
input [SEL_WIDTH-1:0] M0_SEL_I, M1_SEL_I;
output M0_ACK_O, M1_ACK_O;
output M0_ERR_O, M1_ERR_O;
output M0_RTY_O, M1_RTY_O;
output [ADDR_WIDTH-1:0] S_ADR_O;
output [DATA_WIDTH-1:0] S_DAT_O;
input [DATA_WIDTH-1:0] S_DAT_I;
output S_LOCK_O, S_CYC_O;
output S_STB_O, S_WE_O;
output [SEL_WIDTH-1:0] S_SEL_O;
input S_ACK_I, S_ERR_I, S_RTY_I;
reg SELECT_O = 1'bX;
assign M0_DAT_O = ~SELECT_O ? S_DAT_I : 256'dX;
assign M1_DAT_O = SELECT_O ? S_DAT_I : 256'dX;
assign M0_ACK_O = S_ACK_I & ~SELECT_O;
assign M1_ACK_O = S_ACK_I & SELECT_O;
assign M0_ERR_O = S_ERR_I & ~SELECT_O;
assign M1_ERR_O = S_ERR_I & SELECT_O;
assign M0_RTY_O = S_RTY_I & ~SELECT_O;
assign M1_RTY_O = S_RTY_I & SELECT_O;
assign S_ADR_O = SELECT_O ? M1_ADR_I : M0_ADR_I;
assign S_DAT_O = SELECT_O ? M1_DAT_I : M0_DAT_I;
assign S_LOCK_O = SELECT_O ? M1_LOCK_I : M0_LOCK_I;
assign S_CYC_O = SELECT_O ? M1_CYC_I : M0_CYC_I;
assign S_STB_O = SELECT_O ? M1_STB_I : M0_STB_I;
assign S_WE_O = SELECT_O ? M1_WE_I : M0_WE_I;
assign S_SEL_O = SELECT_O ? M1_SEL_I : M0_SEL_I;
wire CYC_END = (S_ACK_I | S_ERR_I | S_RTY_I);
always @(posedge CLK_I)
begin
casex ({S_LOCK_O, CYC_END, M1_CYC_I, M0_CYC_I})
4'b1XXX: SELECT_O <= SELECT_O;
4'b0111: SELECT_O <= ~SELECT_O;
4'b0X10: SELECT_O <= 1'b1;
4'b0X01: SELECT_O <= 1'b0;
4'b0XXX: SELECT_O <= SELECT_O;
endcase
end
endmodule

8
asm/echo.txt Normal file
View File

@ -0,0 +1,8 @@
include "include/memory-map.txt"
_start:
call read_char
call write_char
jump _start
include "lib/rs232.txt"

View File

@ -0,0 +1,18 @@
irq0_mask eql 0x01
irq1_mask eql 0x02
irq2_mask eql 0x04
irq3_mask eql 0x08
irq4_mask eql 0x10
irq5_mask eql 0x20
irq6_mask eql 0x40
irq7_mask eql 0x80
rs232_rx_irq_mask eql irq1_mask
dma_irq_mask eql irq2_mask
rs232_tx_irq_mask eql irq3_mask
timer_irq_mask eql irq4_mask
rs232_rx_isr_addr eql irq_iadr1
dma_isr_addr eql irq_iadr2
rs232_tx_isr_addr eql irq_iadr3
timer_isr_addr eql irq_iadr4

View File

@ -0,0 +1,39 @@
program_base eql 0x00000000
program_size eql 0x00000800
sram_base eql 0x00100000
sram_size eql 0x00100000
irq_iadr0 eql 0xFFFFFF80
irq_iadr1 eql 0xFFFFFF84
irq_iadr2 eql 0xFFFFFF88
irq_iadr3 eql 0xFFFFFF8C
irq_iadr4 eql 0xFFFFFF90
irq_iadr5 eql 0xFFFFFF94
irq_iadr6 eql 0xFFFFFF98
irq_iadr7 eql 0xFFFFFF9C
irq_stat_ctrl eql 0xFFFFFFA0
irq_trigger eql 0xFFFFFFA0
irq_polarity eql 0xFFFFFFA1
irq_mask eql 0xFFFFFFA2
irq_status eql 0xFFFFFFA3
dma_src_addr eql 0xFFFFFFC0
dma_dst_addr eql 0xFFFFFFC4
dma_stat_ctrl eql 0xFFFFFFC8
timer_counter eql 0xFFFFFFE0
timer_reload eql 0xFFFFFFE4
timer_tbu eql 0xFFFFFFE8
timer_tbl eql 0xFFFFFFEC
dev_leds eql 0xFFFFFFF0
dev_switches eql 0xFFFFFFF0
dev_buttons eql 0xFFFFFFF4
dev_display_dp eql 0xFFFFFFF5
dev_display eql 0xFFFFFFF6
rs232_status eql 0xFFFFFFFE
rs232_fifo eql 0xFFFFFFFF

147
asm/lib/base64.txt Normal file
View File

@ -0,0 +1,147 @@
## vim: set sw=3 expandtab:
# ( ... get_char store_byte -- end_char )
# get_char: ( ... -- ... char )
# store_byte: ( ... byte -- ... )
decode_base64:
push
push
immed 0
immed 0
.loop: # ( n bits // gc sb )
rdup
pop
swap
push
swap
push # ( gc // n bits gc sb )
call # ( ch // n bits gc sb )
dup; immed 10; sub
jump .not_nl rel ne drop
drop
pop
pop
jump .loop # ( n bits // gc sb )
.not_nl: # ( ch // n bits gc sb )
dup; immed '='; sub
jump .pad rel eq drop
dup
call decode_char # ( ch val // n bits gc sb )
jump .invalid rel lt
nip # ( val // n bits gc sb )
pop
swap
pop # ( n val bits // gc sb )
or
immed -6
rshift # ( n bits' // gc sb ) [ bits' = (bits|val)<<6 ]
push
immed 6
add # ( n' // bits' gc sb ) [ n' = n + 6 ]
dup; immed 8; sub
jump .store rel ge # ( n' (n'-8) // bits' gc sb )
drop
pop
jump .loop rel # ( n' bits' // gc sb )
.store: # ( n' (n'-8) // bits' gc sb )
nip
dup # ( n'' (n'-8) // bits' gc sb ) [ n'' = n'-8 ]
rdup
pop
swap # ( n'' bits' (n'-8) // bits' gc sb )
immed 6
add
rshift # ( n'' byte // bits' gc sb )
swap
pop
pop
rdup
pop # ( byte n'' bits' gc sb // sb )
swap
push
swap
push
swap
push # ( byte sb // n'' bits' gc sb )
call # ( // n'' bits' gc sb )
pop
pop
jump .loop # ( n'' bits' // gc sb )
.pad: # ( ch // n bits gc sb )
# reset accumulated state
drop
rdrop
rdrop
immed 0
immed 0
# continue until something non-base64 is received
jump .loop # ( n bits // gc sb )
.invalid: # ( ch val // n bits gc sb )
drop
rdrop
rdrop
rdrop
rdrop
return # ( ch // )
# ( val low high -- (low<=val<=high) )
in_range:
over
sub
push # ( val low // (high-low) )
sub
jump .too_low rel lt
pop # ( (val-low) (high-low) // )
sub
jump .too_high rel gt drop
immed 1
return
.too_low: # ( (val-low) // (high-low) )
drop
rdrop
.too_high:
immed 0
return
decode_char:
dup; immed 'A'; immed 'Z'
call in_range
jump .uc rel ne drop
dup; immed 'a'; immed 'z'
call in_range
jump .lc rel ne drop
dup; immed '0'; immed '9'
call in_range
jump .digit rel ne drop
dup; immed '+'; sub
jump .plus rel eq drop
dup; immed '/'; sub
jump .slash rel eq drop
.invalid:
immed -1
nip return
.uc:
immed 65 # 'A' == 65, base64['A'] == 0
sub return
.lc:
immed 71 # 'a' == 97, base64['a'] == 26
sub return
.digit:
immed 4 # '0' == 48, base64['0'] == 52
add return
.plus:
immed 62
nip return
.slash:
immed 63
nip return

48
asm/lib/memcmp.txt Normal file
View File

@ -0,0 +1,48 @@
#int memcmp(const void *data, const void *base, size_t sz)
#{
# const uint8_t *p = (const uint8_t*)data;
# const uint8_t *q = (const uint8_t*)base;
#
# while (sz-- > 0)
# {
# int c = *p++ - *q++;
# if (c != 0)
# return c;
# }
# return 0;
#}
# ( p1 p2 sz -- diff )
memcmp:
jump .nonempty rel ne
drop2
immed 0
nip return # ( 0 // )
.nonempty:
push
dup
push
swap
dup
push # ( p2 p1 // p1 p2 sz )
load byte
swap
load byte # ( [p1] [p2] // p1 p2 sz )
sub
jump .zero rel eq
rdrop
rdrop
rdrop
return # ( diff // )
.zero: # ( diff // p1 p2 sz )
drop
pop
immed 1
add
pop
immed 1
add
pop
immed 1
sub
jump memcpy # ( p1+1 p2+1 sz-1 // )

54
asm/lib/rs232.txt Normal file
View File

@ -0,0 +1,54 @@
include "../include/memory-map.txt"
# Read from serial port w/ remote echo. Ignores CRs.
#
# char read_char()
# {
# uint32_t in;
# char c;
#
# do {
# while (((in = *(uint32_t*)rs232_fifo) & 0x100) == 0)
# /* wait for input */;
# c = in & 0xff;
# } while (c == '\r');
#
# write_char(c);
# return c;
# }
read_char:
immed rs232_fifo
load
dup; immed 0x100; and
jump .got_char rel ne drop
jump read_char rel drop
.got_char:
immed 0xff; and
dup; immed 13; sub
jump .ignore_cr rel eq drop
dup
call write_char
return
.ignore_cr:
jump read_char rel drop
write_char:
dup; immed 10; sub
jump .not_nl rel ne drop
immed 13
pushpc
.not_nl:
immed rs232_status
load byte; immed 2; and
jump .not_nl rel ne drop
immed rs232_fifo
store byte
return
newline:
immed 10
jump write_char
# ^^^ Tail call

32
asm/lib/signed_mult.txt Normal file
View File

@ -0,0 +1,32 @@
## Uses the built-in unsigned multiplier to do signed 32-bit multiplication:
##
## (x+(2**32)x[31])(y+(2**32)y[31]) =
##
## xy + (2**32)(x[31]y + y[31]x) + (2**64)x[31]y[31]
##
## Anything above bit 64 is ignored, so we just need to subtract
##
## x[31]y + y[31]x
##
## from the MSW (bits 32 to 63) of the unsigned result. The LSW is fine as-is.
## The interface is the same as the normal 'mult' instruction.
# ( x y -- lsw msw )
signed_mult:
jump .ypos rel ge
over
jump .xtest rel
.ypos:
immed 0
.xtest:
push
swap
jump .xpos rel ge
over
pop
add
push
.xpos:
mult
pop
sub return

106
asm/logic-test.txt Normal file
View File

@ -0,0 +1,106 @@
include "include/memory-map.txt"
_start:
jump main
include "lib/rs232.txt"
main:
immed 65
dup
call write_char #A 65
immed 1
add
dup
call write_char #B 66
immed -1
sub
dup
call write_char #C 67
immed -1
rshift
immed 66
sub
dup
call write_char #D 68
immed 0
over
sub
immed -1
xor
immed 2
add
dup
call write_char #E 69
immed 2
add
dup
immed 70
nip
call write_char #F 70
dup
call write_char #G 71
loop0:
immed 1
add
dup
call write_char #H-J 72-74
dup
immed 74
sub
jump loop0 rel lt drop
immed 1
add
call write_char #K 75
immed 76
dup
call write_char #L 76
loop1:
immed 1
add
dup
call write_char #M-N 77-78
immed 78
over
sub
jump loop1 rel gt drop
immed 1
or
dup
call write_char #O 79
immed 0b1110000
and
immed 0b10000
or
dup
call write_char #P 80
dup
immed 2
add
push
immed 1
add
call write_char #Q 81
pop
dup
call write_char #R 82
pushpc
pushpc
immed 15
push
rdrop
pop
pop
sub
add
dup
call write_char #S 83
drop
call newline
# Echo input until reset / power-off
echo:
call read_char
call write_char
jump echo rel

268
asm/monitor.txt Normal file
View File

@ -0,0 +1,268 @@
# vim: set sw=3 tw=80:
include "include/memory-map.txt"
export read_char
export write_char
export newline
export read_hex
export write_hex
export puts
export end_of_monitor
import decode_base64
start_of_monitor:
_start:
jump main
include "lib/rs232.txt"
to_upper:
dup; immed 'a'; sub
jump .not_lc rel lt drop
dup; immed 'z'; sub
jump .not_lc rel gt drop
immed 32
sub return
.not_lc:
return
start_str: byte "Stack CPU Monitor Program" 10 0
prompt_str: byte "> " 0
error_str: byte "ERROR" 10 0
access_str: byte "ACCESS DENIED" 10 0
# Commands:
# READ addr
# WRITE value addr
# STORE start_addr
# CALL addr
main:
immed start_str
call puts
.prompt:
immed prompt_str
call puts
.getcmd:
call read_char
call to_upper
dup; immed ' '; sub
jump .ignore_sp rel eq drop
dup; immed 10; sub
jump .ignore_nl rel eq drop
dup; immed 'R'; sub
jump .read rel eq drop
dup; immed 'W'; sub
jump .write rel eq drop
dup; immed 'S'; sub
jump .store rel eq drop
dup; immed 'C'; sub
jump .call rel eq drop
dup; immed 35; sub
jump .comment rel eq drop
drop
.invalid:
call newline
immed error_str
call puts
jump .prompt
.ignore_sp:
jump .getcmd rel drop
.ignore_nl:
jump .prompt rel drop
.expect_char:
call read_char
call to_upper
sub
jump .correct rel eq drop
rdrop # Don't return to caller
jump .invalid rel
.correct:
return
.read:
drop #R
immed 'E'; call .expect_char rel
immed 'A'; call .expect_char rel
immed 'D'; call .expect_char rel
immed ' '; call .expect_char rel
call read_hex
immed 10; sub
jump ..invalid rel ne drop
load
call write_hex
call newline
jump .prompt rel
..invalid:
jump .invalid rel drop
.write:
drop #W
immed 'R'; call .expect_char rel
immed 'I'; call .expect_char rel
immed 'T'; call .expect_char rel
immed 'E'; call .expect_char rel
immed ' '; call .expect_char rel
call read_hex
immed ' '; sub
jump ..invalid1 rel ne drop
call read_hex
immed 10; sub
jump ..invalid2 rel ne drop
dup; immed start_of_monitor; sub
jump ..ok rel lt drop
dup; immed end_of_monitor; sub
jump ..denied rel lt drop
..ok:
store
jump .prompt
..invalid2:
drop
..invalid1:
jump .invalid rel drop
..denied:
drop2
immed access_str
call puts
jump .prompt
.store:
drop #S
immed 'T'; call .expect_char rel
immed 'O'; call .expect_char rel
immed 'R'; call .expect_char rel
immed 'E'; call .expect_char rel
immed ' '; call .expect_char rel
call read_hex
immed 10; sub
jump ..invalid rel ne drop
immed read_char
immed .store_byte
call decode_base64
drop2
jump .prompt rel
..invalid:
jump .invalid rel drop
.store_byte: # ( buffer byte -- buffer' )
over
store byte
immed 1
add return
.call:
drop #C
immed 'A'; call .expect_char rel
immed 'L'; call .expect_char rel
immed 'L'; call .expect_char rel
immed ' '; call .expect_char rel
call read_hex
immed 10; sub
jump ..invalid rel ne drop
call # expected: ( -- )
jump .prompt rel
..invalid:
jump .invalid rel drop
.comment:
immed 10; sub
jump .prompt eq drop
call read_char
jump .comment
# Reads an arbitrary number of hex digits, returning the last eight as
# a 32-bit integer along with the following non-hex character.
read_hex: # ( -- integer last_char )
immed 0
call read_char
call to_upper
dup; push
dup; immed '-'; sub
jump .after_first_char rel ne drop
.negative:
drop
.get_next_char:
call read_char
call to_upper
.after_first_char:
dup; immed '0'; sub
jump .not_hex rel lt drop
dup; immed '9'; sub
jump .number rel le drop
dup; immed 'A'; sub
jump .not_hex rel lt drop
dup; immed 'F'; sub
jump .letter rel le drop
.not_hex:
swap
pop; immed '-'; sub
jump .positive rel ne drop
immed 0; swap; sub
.positive:
swap
return
.number:
immed '0'; sub
.merge:
push
immed -4; rshift
pop
or
jump .get_next_char rel
.letter:
immed 55; sub # 55 = 'A'-10
jump .merge rel
# Displays integer as eight hex digits
write_hex: # ( integer -- )
immed 8
.loop:
push
dup
immed -4; rshift
push
immed 28; rshift
dup; immed 10; sub
jump .letter rel ge drop
immed '0'
.common:
add
call write_char
jump .next rel
.letter:
immed 10; sub
immed 'A'
jump .common rel
.next:
pop
pop
immed 1; sub
jump .loop rel gt
drop2
return
include "lib/base64.txt"
# ( addr -- ) [ addr -> null-terminated ASCII string ]
puts:
dup
load byte
jump .nul rel eq
call write_char
immed 1
add
jump puts rel
.nul:
drop2
return
end_of_monitor:

687
assemble.rb Executable file
View File

@ -0,0 +1,687 @@
#!/usr/bin/env ruby
# vim: set sw=3 tw=80:
require 'base64'
POS_INT_REGEX = /(?:0[0-7]+|0b[01]+|0x[0-9a-fA-F]+|[0-9]+)/
# Make sure input is treated as a signed 32-bit number
def signed32(n)
if n and (n < 0 or n >= 0x80000000)
-(-n & 0xffffffff)
else
n
end
end
def split_immed(n)
result = []
while (n < -16) || (n > 15)
result = [ 0b10000000 | (n & 0x7f) ] + result
n >>= 7
end
[ 0b00100000 | (n & 0x1f) ] + result
end
$labels = {}
$forward_labels = {}
class Label
attr_accessor :name
def initialize(name, pc=nil)
unless name =~ /^\.*[a-zA-Z_]\w*$/
raise StandardError.new("Bad label name '#{name}' at #{$file}:#{$line}")
end
@name, @pc = name, pc
@export, @global = false, false
@alias = nil
end
def export=(x) @export = x ? true : false; end
def export?() @export; end
def global=(x) @global = x ? true : false; end
def global?() @global; end
def local?() (@name =~ /^(\.+)/) ? $1.length : false; end
def level() self.local? || 0; end
def pc=(x) @pc = x; end
def pc() @alias ? @alias.pc : @pc; end
def alias() @alias; end
def alias?() @alias ? true : false; end
def Label.get(name)
lbl = $labels[name] || $forward_labels[name]
if not lbl
lbl = Label.new(name)
$forward_labels[name] = lbl
end
return lbl
end
def Label.define(name)
if $labels.has_key? name
$stderr.puts "WARNING: Redefining label '#{name}'."
end
lbl = $forward_labels[name] || Label.new(name)
$forward_labels.delete name
$labels[name] = lbl
return lbl
end
def Label.alias(label, name)
if $labels.has_key? name
$stderr.puts "WARNING: Redefining label '#{name}'."
end
lbl = $forward_labels[name] || Label.new(name)
lbl.instance_variable_set(:@alias, label)
$forward_labels.delete name
$labels[name] = lbl
return lbl
end
def Label.in_file_scope()
old_labels, old_forwards = $labels.dup, $forward_labels.dup
# Local references end at file boundaries
$labels.delete_if { |k,v| not v.global? }
$forward_labels.delete_if { |k,v| not v.global? }
yield
# Local references end at file boundaries
$labels.delete_if { |k,v| not v.global? }
$forward_labels.delete_if { |k,v| not v.global? }
$labels = old_labels.merge($labels)
$forward_labels = old_forwards.merge($forward_labels)
end
end
class Instruction
def initialize(labels, pc, opcode, args)
@labels = labels.map do |name|
lbl = Label.define(name)
$labels.delete_if { |k,v| v.level > lbl.level }
lbl
end
@type = opcode.downcase.to_sym
@args = args
@align = 1
valid = true
case @type
when :nop, :rshift, :nip, :and, :or,
:xor, :swap, :dup, :add, :sub
valid = args.empty? || ((args.length == 1) && (args[0] =~ /return/i))
@return = !args.empty?
when :load, :store
valid = args.empty? || ((args.length == 1) && (args[0] =~ /byte/i))
@byte_op = !args.empty?
when :return
valid = args.empty?
@type = :nop
@return = true
when :rdrop, :pop, :pushpc, :push, :rdup, :mult, :over, :nlz
valid = args.empty?
when :immed
if (args.length == 1) and (args[0] =~ /^-?#{POS_INT_REGEX}$/)
# Integer constant
valid, @value = true, signed32(args[0].to_i(0))
elsif (args.length == 1) and (args[0] =~ /^'.'$/)
# Character constant
valid, @value = true, args[0][1].ord
else
valid, @target = true, Label.get(args[0])
end
when :jump, :call
@rel, @target, @cc, @drop = false, nil, :always, false
args.each do |arg|
case arg
when /^rel$/i
@rel = true
when /^(never|eq|lt|gt|always|ne|ge|le)$/i
@cc = $1.downcase.to_sym
when /^drop$/i
@drop = true;
else
if @target
raise StandardError.new("Syntax error: #{opcode} #{args.join(' ')}")
else
@target = Label.get(arg)
end
end
end
when :drop
@type = :jump
@rel = false
@target = nil
@cc = :never
@drop = false
when :drop2
@type = :jump
@target = nil
@rel = false
@cc = :never
@drop = true
when :merge
valid = (args.length == 1) && (args[0] =~ /^#{POS_INT_REGEX}$/)
@value = args[0].to_i(0) if valid
valid &&= (@value >= 0 and @value <= 127)
when :resb
valid = (args.length == 1) && (args[0] =~ /^#{POS_INT_REGEX}$/)
@immed = [ 0 ] * args[0].to_i(0) if valid
when :align
valid = (args.length == 1) && (args[0] =~ /^#{POS_INT_REGEX}$/)
@align = args[0].to_i(0) if valid
valid &&= (@align >= 1)
when :byte
valid = true
@immed = []
args.each do |arg|
case arg
when /^-?#{POS_INT_REGEX}$/
v = arg.to_i(0)
valid &&= (-128..255).member? v
@immed << (v & 0xff) if valid
when /^'(.)'$/
@immed << (arg[1].ord & 0xff) if valid
when /^".*"$/
arg[1..-2].scan(/([^\\"])|(\\")|(\\\\)/) do |a,b,c|
a = '"' if b
a = '\\' if c
@immed << (a[0].ord & 0xff)
end
else
raise StandardError.new("Unsupported BYTE operand.")
end
end
when :word
valid = true
@immed = []
@align = 4
args.each do |arg|
case arg
when /^-?#{POS_INT_REGEX}$/
v = arg.to_i(0)
# Convert long to unsigned chars w/ big-endian byte order
@immed += [v].pack("N").unpack("C*")
else
raise StandardError.new("WORD only supports integer operands.")
end
end
else
raise StandardError.new("Syntax error: #{opcode} #{args.join(' ')}")
end
if not valid
raise StandardError.new("Invalid instruction: #{opcode} #{args.join(' ')}")
end
reflow(pc)
end
def reflow(pc)
if pc >= $options[:ram_size]
raise StandardError.new("No more room in Block RAM.")
end
@align_bytes = (-pc) % @align
@pc = pc + @align_bytes
old_bytes = @bytes
@labels.each { |lbl| lbl.pc = @pc }
if @target
@value = signed32(@target.pc)
if @value
if @rel
# Known relative; PC of jump/call depends on immed. length
diff = @value - (@pc + 6)
im_size = split_immed(diff).length
diff = @value - (@pc + im_size + 1)
@immed = split_immed(diff)
if @immed.length > im_size
im_size = @immed.length
diff = @value - (@pc + im_size + 1)
@immed = split_immed(diff)
end
if @immed.length > im_size
raise StandardError.new("Relative target offset won't converge!")
end
else
# Absolute known address
@immed = split_immed(@value)
end
else
# Unknown address; reserve maximum space that may be required
@immed = split_immed(-($max_pc+1))
end
elsif @value
@immed = split_immed(@value)
end
if @type == :align
@bytes = 0
elsif not @immed
@bytes = 1
elsif @type == :jump or @type == :call
@bytes = @immed.length + 1
else
@bytes = @immed.length
end
@bytes += @align_bytes
if old_bytes != @bytes
$layout_changed = true
end
end
def bytes() @bytes; end
def finalize_target
if @target
@value = signed32(@target.pc)
if not @value
raise StandardError.new("Unknown label: '#{@target.name}'")
else
if @rel
@immed = split_immed(@value - (@pc + @bytes))
else
@immed = split_immed(@value)
end
end
if @type == :immed
bytes = @immed.length
else
bytes = @immed.length + 1
end
if @bytes < bytes
raise StandardError.new("Insufficient space for target address.")
end
end
end
OPCODES = {
:nop => 0b00000000,
:rshift => 0b00000010,
:add => 0b00000100,
:sub => 0b00000110,
:nip => 0b00001000,
:and => 0b00001010,
:or => 0b00001100,
:xor => 0b00001110,
:swap => 0b00010000,
:dup => 0b00010010,
:over => 0b00010100,
:rdup => 0b00010101,
:nlz => 0b00010110,
:mult => 0b00010111,
:load => 0b00011000,
:store => 0b00011010,
:rdrop => 0b00011100,
:pop => 0b00011101,
:pushpc => 0b00011110,
:push => 0b00011111,
:jump => 0b01000000,
:call => 0b01100000
}.freeze
CC_MAP = {
:never => 0b000,
:eq => 0b001,
:lt => 0b010,
:gt => 0b011,
:always => 0b100,
:ne => 0b101,
:ge => 0b110,
:le => 0b111
}.freeze
def to_bytes
finalize_target
bytes = [0] * @align_bytes
case @type
when :align
# no opcode
nil
when :nop, :rshift, :add, :sub, :nip,
:and, :or, :xor, :swap, :dup
bytes << (OPCODES[@type] | (@return ? 1 : 0))
when :load, :store
bytes << (OPCODES[@type] | (@byte_op ? 1 : 0))
when :rdrop, :pop, :pushpc, :push, :rdup, :mult, :over, :nlz
bytes << OPCODES[@type]
when :immed, :resb, :byte, :word
bytes += @immed
when :jump, :call
result = OPCODES[@type]
result |= 0b10000 if @rel
result |= (CC_MAP[@cc]<<1) if @cc
result |= 0b00001 if @drop
# Insert NOPs if immed is shorter than allocated
pad = @immed && ([0] * (@bytes - (@immed.length + 1)))
bytes += pad if pad
bytes += @immed if @immed
bytes << result
when :merge
bytes << (0b10000000 | (@value & 0x7f))
else
raise StandardError.new("Don't know how to output a #{@type} opcode.")
end
bytes
end
def to_s
finalize_target
s = @labels.map {|lbl| lbl.name + ":\n" }.join('')
s << " #{ADDR_FMT % @pc} "
case @type
when :align
s << ('%s %d' % [ @type, @align ])
when :nop, :rshift, :add, :sub, :nip,
:and, :or, :xor, :swap, :dup
s << @type.to_s
s << ' return' if @return
when :load, :store
s << @type.to_s
s << ' byte' if @byte_op
when :rdrop, :pop, :pushpc, :push, :rdup, :mult, :over, :nlz
s << @type.to_s
when :immed
s << @type.to_s
s << (' 0x%-8X' % (@value & 0xffffffff))
s << ' # ' << @value.to_s << ' decimal'
when :jump, :call
s << @type.to_s
s << ' ' << @target.name if @target
s << ' (' << (ADDR_FMT % @value) << ')' if @value
s << ' rel' if @rel
s << ' ' << @cc.to_s if @cc
s << ' drop' if @drop
when :merge
s << @type.to_s << (' 0b%07b' % @value)
when :resb
s << @type.to_s << ' ' << @immed.length.to_s
when :byte
s << @type.to_s << ' ' << @immed.join(' ')
when :word
words = @immed.pack('C*').unpack('N*').map { |w| '0x%08x' % w }
s << @type.to_s << ' ' << words.join(' ')
else
raise StandardError.new("Don't know how to output a #{@type} opcode.")
end
s
end
end
def parse_options(argv)
options = {
:include_path => [],
:files => []
}
ARGV.each_with_index do |option,idx|
case option
when /^--origin=(#{POS_INT_REGEX})$/
options[:origin] = $1.to_i(0)
when /^--format=(binary|hex)$/
options[:format] = $1.to_sym
options[:pad] = true unless options.has_key? :pad
when /^--format=(raw|write|base64)$/
options[:format] = $1.to_sym
when /^-p|--pad$/
options[:pad] = true
when /^-r|--no-pad|--raw$/
options[:pad] = false
when /^--(?:code|disassembly)=(.*)$/
options[:code_file] = ($1=='-') ? $stdout : File.open($1, "w")
when /^--sym(?:bol)?s=(.*)$/
options[:symbol_file] = ($1=='-') ? $stdout : File.open($1, "w")
when /^--ram(?:-size)?=(#{POS_INT_REGEX})$/
options[:ram_size] = $1.to_i(0)
when /^--partial$/
options[:code_file] ||= $stdout
options[:partial] = true
when /^(?:--includes=|-I)(.*)$/
options[:include_path] << File.expand_path($1)
when /^--depends$/
options[:depends_file] = $stdout
when /^--$/
options[:files] += ARGV[(idx+1) .. -1]
break
when /^-/
raise StandardError.new("Unknown option '#{option}'.")
else
options[:files] << option
end
end
return options
end
$options = {
# Defaults
:origin => 0x400,
:format => :base64,
:pad => false,
:code_file => nil,
:symbol_file => nil,
:ram_size => (4 * 2048),
:partial => false,
:depends_file => false
}.merge(parse_options(ARGV)).freeze
$pc = $options[:origin]
$max_pc = $options[:ram_size] - 1
$instructions = []
$accum_labels = []
OPERAND = /(?:'.'|"(?:[^\\"]|\\"|\\\\)*"|\S+)/
ADDR_FMT = "0x%0#{$max_pc.to_s(16).length}x"
$included_files = []
def read_asm_file(fname)
old_file, old_line, $file, $line = $file, $line, fname, 1
Label.in_file_scope do
File.open(fname).each_line do |ln|
ln.sub!(/#.*$/,'')
ln.split(';').each do |part|
while part.sub!(/^\s*(\S+)\s*:\s*/, '')
$accum_labels << $1
end
case part
when /^\s*$/
nil # Empty line or comment
when /^\s*include\s+"([^"]+)"\s*$/
inc_name = $1
search_path = [File.dirname(fname)] + $options[:include_path]
found = nil
search_path.each do |dir|
try_name = File.expand_path(inc_name, dir)
if File.exist? try_name
found = try_name
break
end
end
if found
$included_files << found
read_asm_file(found)
else
raise StandardError.new("Unable to locate include file '#{inc_name}'.")
end
when /^\s*export\s+(#{OPERAND}(?:\s*#{OPERAND})*)\s*$/
$1.scan(OPERAND).each do |sym|
if sym =~ /^\./
raise StandardError.new("Can't export local label '#{sym}'.")
else
lbl = Label.get(sym)
lbl.export = true
lbl.global = true
end
end
when /^\s*import\s+(#{OPERAND}(?:\s*#{OPERAND})*)\s*$/
$1.scan(OPERAND).each do |sym|
if sym =~ /^\./
raise StandardError.new("Can't import local label '#{sym}'.")
else
Label.get(sym).global = true
end
end
when /^\s*(#{OPERAND})\s+eql\s+(#{OPERAND})\s*$/i
name, value = $1, $2
if value =~ /^-?#{POS_INT_REGEX}$/
value = value.to_i(0)
unless $labels[name] and $labels[name].pc == value
lbl = Label.define(name)
lbl.pc = value
lbl.global = !lbl.local?
end
else
lbl = Label.alias(Label.get(value), name)
lbl.global = !lbl.local?
end
when /^\s*(\S+)((?:\s+#{OPERAND})*)\s*$/
ins = Instruction.new($accum_labels,$pc,$1,$2.scan(/#{OPERAND}/))
$accum_labels = []
$instructions << ins
$pc += ins.bytes
end
end
$line += 1
end
end
$file, $line = old_file, old_line
end
begin
$options[:files].each { |fname| read_asm_file(fname) }
rescue => e
$stderr.puts "Error at #{$file} line #{$line}:"
$stderr.puts e
$stderr.puts e.backtrace
end
if $options[:depends_file]
$options[:depends_file].puts $included_files
exit
end
if $instructions.empty? or not $accum_labels.empty?
ins = Instruction.new($accum_labels, $pc, "align", ["1"])
$instructions << ins
$pc += ins.bytes
end
5.times do
$max_pc = $pc
$pc = $options[:origin]
$layout_changed = false
$instructions.each { |ins| ins.reflow($pc); $pc += ins.bytes }
break unless $layout_changed
end
if $layout_changed
$stderr.puts "WARNING: Opcode layout may be suboptimal."
end
if $options[:code_file]
$instructions.each do |ins|
$options[:code_file].puts ins
end
end
$max_length = $labels.values.inject(0) do |max, lbl|
len = lbl.name.length
if lbl.export? and (len > max)
len
else
max
end
end
if $options[:symbol_file]
file = $options[:symbol_file]
($labels.values.sort {|a,b| a.pc <=> b.pc }).each do |lbl|
if lbl.pc and lbl.export?
file.printf "%-*s EQL 0x%08x\n", $max_length, lbl.name, lbl.pc
end
end
end
unless $options[:partial]
bytes = ($instructions.map { |ins| ins.to_bytes }).flatten
if $options[:pad] and [ :raw, :binary, :hex ].member? $options[:format]
bytes = ([0] * $options[:origin]) + bytes
bytes += ([0] * ($options[:ram_size] - bytes.length))
end
file = $stdout
case $options[:format]
when :raw
file.write bytes.pack('C*')
when :base64
file.puts('STORE %X' % $options[:origin])
file.print Base64.encode64(bytes.pack('C*'))
file.puts '.'
($labels.values.sort {|a,b| a.name <=> b.name }).each do |lbl|
if lbl.pc and lbl.export?
file.printf "# %-*s EQL 0x%08x\n", $max_length, lbl.name, lbl.pc
end
end
when :binary, :hex, :write
pc = $options[:origin]
bytes = ([0] * (pc & 0x3)) + bytes
pc &= ~0x3
words = (bytes+[0,0,0]).pack('C*').unpack('N*')
addr_len = $max_pc.to_s(16).length
words.each do |w|
if $options[:format] == :write
file.printf "WRITE %08X %0*X\n", w, addr_len, pc
elsif $options[:format] == :binary
file.printf "%032b\n", w
else # hex
file.printf "%08X\n", w
end
pc += 4
end
else
raise StandardError.new("Unknown output format")
end
end

346
cpu.v Normal file
View File

@ -0,0 +1,346 @@
// synthesis attribute slice_utilization_ratio of StackCPU is 50;
module StackCPU (CLK_I, RST_I, IRQ_I, IACK_O, IADR_I,
INST_ADR_O, INST_DAT_I, INST_CYC_O, INST_STB_O, INST_ACK_I,
DATA_ADR_O, DATA_DAT_I, DATA_DAT_O, DATA_CYC_O, DATA_STB_O, DATA_WE_O,
DATA_SEL_O, DATA_ACK_I);
input CLK_I;
input RST_I;
input IRQ_I;
output IACK_O;
input [31:0] IADR_I;
output [31:0] INST_ADR_O;
input [7:0] INST_DAT_I;
output INST_CYC_O;
output INST_STB_O;
input INST_ACK_I;
output [31:2] DATA_ADR_O;
input [31:0] DATA_DAT_I;
output [31:0] DATA_DAT_O;
output DATA_CYC_O;
output DATA_STB_O;
output DATA_WE_O;
output [3:0] DATA_SEL_O;
input DATA_ACK_I;
reg [31:0] PC = 32'd0; /* address of current instr. */
reg [31:0] T = 32'd0; /* Top of (data) stack */
reg [31:0] N = 32'd0; /* Next of (data) stack */
reg [31:0] R = 32'd0; /* top of Return stack */
reg [8:0] SP = 9'd0; /* Stack Pointer */
reg [8:0] RP = 9'd0; /* Return stack Pointer */
wire [31:0] INST_ADR_O = PC;
reg INST_CYC_O = 1'b1;
reg INST_STB_O = 1'b1;
wire [31:2] DATA_ADR_O = T[31:2];
reg [31:0] DATA_DAT_O;
reg DATA_CYC_O = 1'b0;
reg DATA_STB_O = 1'b0;
reg DATA_WE_O = 1'b0;
reg [3:0] DATA_SEL_O = 4'b0000;
wire [31:0] STACK_M1_OUT;
wire [31:0] STACK_M2_OUT;
wire [31:0] RETURN_OUT;
wire CMP_EQ = ~|N;
wire CMP_LT = N[31];
wire CMP_GT = ~(CMP_EQ|CMP_LT);
reg CMP_TRUE;
StackRAM DataStack1 (
.CLKIN(CLK_I),
.ADDR_A(SP),
.WE_A(1'b1),
.IN_A(N),
.ADDR_B(SP - 9'd1),
.OUT_B(STACK_M1_OUT)
);
StackRAM DataStack2 (
.CLKIN(CLK_I),
.ADDR_A(SP),
.WE_A(1'b1),
.IN_A(N),
.ADDR_B(SP - 9'd2),
.OUT_B(STACK_M2_OUT)
);
StackRAM ReturnStack (
.CLKIN(CLK_I),
.ADDR_A(RP),
.WE_A(1'b1),
.IN_A(R),
.ADDR_B(RP - 9'd1),
.OUT_B(RETURN_OUT)
);
wire [5:0] NLZ_OUT;
NLZ NLZ_inst (
.IN(T),
.OUT(NLZ_OUT)
);
reg DO_IRQ = 1'b0;
wire IACK_O = IRQ_I & DO_IRQ;
reg USE_SAVED_IR = 1'b0;
reg [7:0] SAVED_IR;
wire [7:0] IR = USE_SAVED_IR ? SAVED_IR : INST_DAT_I;
wire IR_AVAIL = (USE_SAVED_IR | INST_ACK_I) & ~DO_IRQ;
wire IS_DATA = (IR[7:2] == 6'b000110);
reg USE_SAVED_DATA = 1'b0;
reg [31:0] SAVED_DATA;
wire [31:0] DATA_IN = USE_SAVED_DATA ? SAVED_DATA : DATA_DAT_I;
wire DATA_ACKED = (USE_SAVED_DATA | DATA_ACK_I);
reg [7:0] BYTE_IN;
always @*
begin
casex (IR[3:1])
3'b000: /* never */ CMP_TRUE <= 1'b0;
3'b001: /* equal */ CMP_TRUE <= CMP_EQ;
3'b010: /* less */ CMP_TRUE <= CMP_LT;
3'b011: /* greater */ CMP_TRUE <= CMP_GT;
3'b100: /* always */ CMP_TRUE <= 1'b1;
3'b101: /* !equal */ CMP_TRUE <= !CMP_EQ;
3'b110: /* !less */ CMP_TRUE <= !CMP_LT;
3'b111: /* !greater */ CMP_TRUE <= !CMP_GT;
endcase
DATA_CYC_O <= IR_AVAIL & IS_DATA & ~USE_SAVED_DATA;
DATA_STB_O <= IR_AVAIL & IS_DATA & ~USE_SAVED_DATA;
DATA_WE_O <= IR[1]; /* 1 if STORE */
casex ({IR[0], T[1:0]})
3'b0XX: DATA_SEL_O <= 4'b1111;
3'b100: DATA_SEL_O <= 4'b1000;
3'b101: DATA_SEL_O <= 4'b0100;
3'b110: DATA_SEL_O <= 4'b0010;
3'b111: DATA_SEL_O <= 4'b0001;
endcase
casex ({IR[0], T[1:0]})
3'b0XX: DATA_DAT_O <= N;
3'b100: DATA_DAT_O <= { N[7:0], 24'h000000 };
3'b101: DATA_DAT_O <= { 8'h00, N[7:0], 16'h0000 };
3'b110: DATA_DAT_O <= { 16'h0000, N[7:0], 8'h00 };
3'b111: DATA_DAT_O <= { 24'h000000, N[7:0] };
endcase
casex (T[1:0])
2'b00: BYTE_IN <= DATA_IN[31:24];
2'b01: BYTE_IN <= DATA_IN[23:16];
2'b10: BYTE_IN <= DATA_IN[15: 8];
2'b11: BYTE_IN <= DATA_IN[ 7: 0];
endcase
end
wire IS_MULT = (IR == 8'b00010111);
reg [63:0] MULT_OUT;
reg MULT_READY = 1'b0;
wire DATA_WAIT = IS_DATA & ~DATA_ACKED;
wire MULT_WAIT = IS_MULT & ~MULT_READY;
wire OPCODE_COMPLETE = IR_AVAIL & ~DATA_WAIT & ~MULT_WAIT;
always @(posedge CLK_I)
begin
MULT_OUT <= $unsigned(N) * $unsigned(T);
if (RST_I)
begin
PC <= 32'd0;
SP <= 9'd0;
RP <= 9'd0;
T <= 32'd0;
N <= 32'd0;
R <= 32'd0;
USE_SAVED_IR <= 1'b0;
USE_SAVED_DATA <= 1'b0;
INST_CYC_O <= 1'b0;
INST_STB_O <= 1'b0;
MULT_READY <= 1'b0;
DO_IRQ <= 1'b0;
end
else
begin
if (DO_IRQ)
begin
// Simulate a call to the address in IADR_I
USE_SAVED_IR <= 1'b0;
USE_SAVED_DATA <= 1'b0;
INST_CYC_O <= 1'b1;
INST_STB_O <= 1'b1;
MULT_READY <= 1'b0;
DO_IRQ <= 1'b0;
SP <= SP;
RP <= RP+1;
T <= T;
N <= N;
R <= PC;
PC <= IADR_I;
end
else if (OPCODE_COMPLETE)
begin
USE_SAVED_IR <= 1'b0;
USE_SAVED_DATA <= 1'b0;
INST_CYC_O <= ~IRQ_I;
INST_STB_O <= ~IRQ_I;
MULT_READY <= 1'b0;
DO_IRQ <= IRQ_I;
casex (IR)
8'b0000001X: /* RSHIFT */ SP <= SP-1;
8'b000001XX: /* ADD/SUB */ SP <= SP-1;
8'b00001XXX: /* LOGIC */ SP <= SP-1;
8'b0001001X: /* DUP */ SP <= SP+1;
8'b00010100: /* OVER */ SP <= SP+1;
8'b0001101X: /* STORE */ SP <= SP-2;
8'b00011101: /* POP */ SP <= SP+1;
8'b00011111: /* PUSH */ SP <= SP-1;
8'b001XXXXX: /* IMMED */ SP <= SP+1;
8'b01XXXXX0: /* JUMP/CALL */ SP <= SP-1;
8'b01XXXXX1: /* J/C DROP */ SP <= SP-2;
8'bXXXXXXXX: /* other */ SP <= SP;
endcase
casex (IR)
8'b00010101: /* RDUP */ RP <= RP+1;
8'b00010111: /* MULT */ RP <= RP;
8'b00011001: /* LOAD BYTE */ RP <= RP;
8'b00011011: /* STORE BYTE */ RP <= RP;
8'b00011100: /* RDROP */ RP <= RP-1;
8'b00011101: /* POP */ RP <= RP-1;
8'b00011110: /* PUSHPC */ RP <= RP+1;
8'b00011111: /* PUSH */ RP <= RP+1;
8'b001XXXXX: /* IMMED */ RP <= RP;
8'b010XXXXX: /* JUMP */ RP <= RP;
8'b011XXXXX: /* CALL */ RP <= CMP_TRUE ? (RP+1) : RP;
8'b1XXXXXXX: /* MERGE */ RP <= RP;
8'bXXXXXXX1: /* RETURN */ RP <= RP-1;
8'bXXXXXXXX: /* other */ RP <= RP;
endcase
casex (IR)
8'b0000000X: /* NOP */ T <= T;
8'b0000001X: /* RSHIFT */ T <= T[31] ? (N << (-T[5:0]))
: (N >> T[5:0]);
8'b0000010X: /* ADD */ T <= N + T;
8'b0000011X: /* SUB */ T <= N - T;
8'b0000100X: /* NIP */ T <= T;
8'b0000101X: /* AND */ T <= N & T;
8'b0000110X: /* OR */ T <= N | T;
8'b0000111X: /* XOR */ T <= N ^ T;
8'b0001000X: /* SWAP */ T <= N;
8'b0001001X: /* DUP */ T <= T;
8'b00010100: /* OVER */ T <= N;
8'b00010101: /* RDUP */ T <= T;
8'b00010110: /* NLZ */ T <= NLZ_OUT;
8'b00010111: /* MULT */ T <= MULT_OUT[63:32];
8'b00011000: /* LOAD */ T <= DATA_IN;
8'b00011001: /* LOAD BYTE */ T <= BYTE_IN;
8'b0001101X: /* STORE */ T <= STACK_M1_OUT;
8'b00011100: /* RDROP */ T <= T;
8'b00011101: /* POP */ T <= R;
8'b00011110: /* PUSHPC */ T <= T;
8'b00011111: /* PUSH */ T <= N;
8'b001XXXXX: /* IMMED */ T <= $signed(IR[4:0]);
8'b01XXXXX0: /* JUMP/CALL */ T <= N;
8'b01XXXXX1: /* J/C DROP */ T <= STACK_M1_OUT;
8'b1XXXXXXX: /* MERGE */ T <= (T<<7) | IR[6:0];
endcase
casex (IR)
8'b0000000X: /* NOP */ N <= N;
8'b0000001X: /* RSHIFT */ N <= STACK_M1_OUT;
8'b0000010X: /* ADD */ N <= STACK_M1_OUT;
8'b0000011X: /* SUB */ N <= STACK_M1_OUT;
8'b0000100X: /* NIP */ N <= STACK_M1_OUT;
8'b0000101X: /* AND */ N <= STACK_M1_OUT;
8'b0000110X: /* OR */ N <= STACK_M1_OUT;
8'b0000111X: /* XOR */ N <= STACK_M1_OUT;
8'b0001000X: /* SWAP */ N <= T;
8'b0001001X: /* DUP */ N <= T;
8'b00010100: /* OVER */ N <= T;
8'b00010101: /* RDUP */ N <= N;
8'b00010110: /* NLZ */ N <= N;
8'b00010111: /* MULT */ N <= MULT_OUT[31:0];
8'b0001100X: /* LOAD */ N <= N;
8'b0001101X: /* STORE */ N <= STACK_M2_OUT;
8'b00011100: /* RDROP */ N <= N;
8'b00011101: /* POP */ N <= T;
8'b00011110: /* PUSHPC */ N <= N;
8'b00011111: /* PUSH */ N <= STACK_M1_OUT;
8'b001XXXXX: /* IMMED */ N <= T;
8'b01XXXXX0: /* JUMP/CALL */ N <= STACK_M1_OUT;
8'b01XXXXX1: /* J/C DROP */ N <= STACK_M2_OUT;
8'b1XXXXXXX: /* MERGE */ N <= N;
endcase
casex (IR)
8'b00010101: /* RDUP */ R <= R;
8'b00010111: /* MULT */ R <= R;
8'b00011001: /* LOAD BYTE */ R <= R;
8'b00011011: /* STORE BYTE */ R <= R;
8'b00011100: /* RDROP */ R <= RETURN_OUT;
8'b00011101: /* POP */ R <= RETURN_OUT;
8'b00011110: /* PUSHPC */ R <= PC+1;
8'b00011111: /* PUSH */ R <= T;
8'b001XXXXX: /* IMMED */ R <= R;
8'b010XXXXX: /* JUMP */ R <= R;
8'b011XXXXX: /* CALL */ R <= CMP_TRUE ? (PC+1) : R;
8'b1XXXXXXX: /* MERGE */ R <= R;
8'bXXXXXXX1: /* RETURN */ R <= RETURN_OUT;
8'bXXXXXXXX: /* other */ R <= R;
endcase
casex (IR)
8'b00010101: /* RDUP */ PC <= PC + 1;
8'b00010111: /* MULT */ PC <= PC + 1;
8'b00011001: /* LOAD BYTE */ PC <= PC + 1;
8'b00011011: /* STORE BYTE */ PC <= PC + 1;
8'b00011101: /* POP */ PC <= PC + 1;
8'b00011111: /* PUSH */ PC <= PC + 1;
8'b001XXXXX: /* IMMED */ PC <= PC + 1;
8'b01X0XXXX: /* JUMP/CALL */ PC <= CMP_TRUE ? T : (PC+1);
8'b01X1XXXX: /* " REL */ PC <= CMP_TRUE ? ((PC+1)+T) : (PC+1);
8'b1XXXXXXX: /* MERGE */ PC <= PC + 1;
8'bXXXXXXX1: /* RETURN */ PC <= R;
8'bXXXXXXXX: /* other */ PC <= PC + 1;
endcase
end
else
begin
INST_CYC_O <= ~IR_AVAIL;
INST_STB_O <= ~IR_AVAIL;
if (INST_ACK_I)
begin
SAVED_IR <= INST_DAT_I;
USE_SAVED_IR <= 1'b1;
end
if (DATA_ACK_I)
begin
SAVED_DATA <= DATA_DAT_I;
USE_SAVED_DATA <= 1'b1;
end
MULT_READY <= 1'b1;
end
end
end
endmodule

161
dma.v Normal file
View File

@ -0,0 +1,161 @@
// vim:set sw=3:
// Source modes: Destination modes:
// constant data discard
// constant address constant address
// decrementing address decrementing address
// incrementing address incrementing address
// Constant source data is for initialization (e.g. zero-fill).
// Discard destination is for computations (e.g. CRC [future]).
// All transfers are word-aligned -- no SEL_O signals are generated.
// Control interface:
// 0x00 source address (R/W)
// 0x04 dest. address (R/W)
// 0x08 status/control (RO)
// IRQ_O is edge-triggered, active-high. 0->1 indicates completion.
// synthesis attribute slice_utilization_ratio of WB_DMA is 15;
module WB_DMA (CLK_I, RST_I, IRQ_O,
CTL_ADR_I, CTL_DAT_I, CTL_DAT_O, CTL_STB_I, CTL_WE_I, CTL_ACK_O,
SRC_ADR_O, SRC_DAT_I, SRC_STB_O, SRC_WE_O, SRC_ACK_I,
DST_ADR_O, DST_DAT_O, DST_STB_O, DST_WE_O, DST_ACK_I);
parameter ADDR_WIDTH = 30;
parameter DATA_WIDTH = 32;
input CLK_I, RST_I;
output IRQ_O;
input CTL_STB_I, CTL_WE_I;
input [1:0] CTL_ADR_I;
input [31:0] CTL_DAT_I;
output [31:0] CTL_DAT_O;
output CTL_ACK_O;
output [ADDR_WIDTH-1:0] SRC_ADR_O;
input [DATA_WIDTH-1:0] SRC_DAT_I;
output SRC_STB_O, SRC_WE_O;
input SRC_ACK_I;
output [ADDR_WIDTH-1:0] DST_ADR_O;
output [DATA_WIDTH-1:0] DST_DAT_O;
output DST_STB_O, DST_WE_O;
input DST_ACK_I;
reg [DATA_WIDTH-1:0] SRC_ADR_REG;
reg [DATA_WIDTH-1:0] DST_ADR_REG;
reg [1:0] SRC_TYPE, DST_TYPE;
reg [16:0] WORD_COUNT = 0;
wire CMD_WRITE = CTL_STB_I & CTL_WE_I & (CTL_ADR_I == 2'h2);
wire CONSTANT = ~|SRC_TYPE;
wire DISCARD = ~|DST_TYPE;
wire CANCEL = CMD_WRITE & CTL_DAT_I[1];
wire START = CMD_WRITE & CTL_DAT_I[0];
wire RUNNING = |WORD_COUNT & ~RST_I;
wire DONE;
FIFO #(
.QUEUE_SIZE(2),
.DATA_WIDTH(32)
) TransferFIFO (
.CLK_I(CLK_I),
.RST_I(RST_I | CANCEL),
.DAT_I(CONSTANT ? SRC_ADR_REG : SRC_DAT_I),
.DAT_O(DST_DAT_O),
.QUEUE_I(QUEUE),
.DEQUEUE_I(DEQUEUE),
.EMPTY_O(FIFO_EMPTY),
.FULL_O(FIFO_FULL)
);
assign QUEUE = CONSTANT ? (RUNNING & ~FIFO_FULL) : (SRC_STB_O & SRC_ACK_I);
assign DEQUEUE = DISCARD ? ~FIFO_EMPTY : (DST_STB_O & DST_ACK_I);
assign DONE = ~RUNNING & FIFO_EMPTY;
assign IRQ_O = DONE;
assign SRC_ADR_O = SRC_ADR_REG[DATA_WIDTH-1:DATA_WIDTH-ADDR_WIDTH];
assign DST_ADR_O = DST_ADR_REG[DATA_WIDTH-1:DATA_WIDTH-ADDR_WIDTH];
assign SRC_STB_O = RUNNING & ~CONSTANT & ~FIFO_FULL;
assign DST_STB_O = ~DISCARD & ~FIFO_EMPTY & ~RST_I;
assign SRC_WE_O = 1'b0;
assign DST_WE_O = 1'b1;
assign CTL_ACK_O = CTL_STB_I;
reg [31:0] CTL_DAT_O;
wire [31:0] STATUS = {
WORD_COUNT[15:0],
4'h0,
{ 2'b0, SRC_TYPE },
{ 2'b0, DST_TYPE },
{ 3'b0, ~DONE }
};
always @*
begin
case (CTL_ADR_I)
2'h0: CTL_DAT_O <= SRC_ADR_REG;
2'h1: CTL_DAT_O <= DST_ADR_REG;
2'h2: CTL_DAT_O <= STATUS;
2'h3: CTL_DAT_O <= 32'dX;
endcase
end
always @(posedge CLK_I)
begin
if (RST_I | CANCEL)
begin
WORD_COUNT <= 0;
end
else if (DONE)
begin
if (CTL_STB_I & CTL_WE_I)
begin
case (CTL_ADR_I)
2'h0: SRC_ADR_REG <= CTL_DAT_I;
2'h1: DST_ADR_REG <= CTL_DAT_I;
endcase
end
if (START)
begin
// word count of zero means 2^16 == 0x10000 words.
WORD_COUNT <= { ~|CTL_DAT_I[31:16], CTL_DAT_I[31:16] };
SRC_TYPE <= CTL_DAT_I[9:8];
DST_TYPE <= CTL_DAT_I[5:4];
end
end
else
begin
if (QUEUE)
begin
WORD_COUNT <= WORD_COUNT - 1;
case (SRC_TYPE)
2'b00: SRC_ADR_REG <= SRC_ADR_REG;
2'b01: SRC_ADR_REG <= SRC_ADR_REG;
2'b10: SRC_ADR_REG <= SRC_ADR_REG - (DATA_WIDTH/8);
2'b11: SRC_ADR_REG <= SRC_ADR_REG + (DATA_WIDTH/8);
endcase
end
if (DEQUEUE)
begin
case (DST_TYPE)
2'b00: DST_ADR_REG <= DST_ADR_REG;
2'b01: DST_ADR_REG <= DST_ADR_REG;
2'b10: DST_ADR_REG <= DST_ADR_REG - (DATA_WIDTH/8);
2'b11: DST_ADR_REG <= DST_ADR_REG + (DATA_WIDTH/8);
endcase
end
end
end
endmodule

33
doc/opcode-overview.txt Normal file
View File

@ -0,0 +1,33 @@
NOP rtn:1 ( -- )
RSHIFT rtn:1 ( a b -- (a>>b) )
ADD rtn:1 ( a b -- (a+b) )
SUB rtn:1 ( a b -- (a-b) )
NIP rtn:1 ( a b -- b )
AND rtn:1 ( a b -- (a&b) )
OR rtn:1 ( a b -- (a|b) )
XOR rtn:1 ( a b -- (a^b) )
SWAP rtn:1 ( a b -- b a )
DUP rtn:1 ( a -- a a )
OVER ( a b -- a b a )
RDUP ( -- ) R( ra -- ra ra )
NLZ ( a -- nlz(a) )
MULT ( a b -- msw lsw )
LOAD byte:1 ( a -- mem[a] )
STORE byte:1 ( a b -- )
RDROP ( -- ) R( a -- )
POP ( -- a ) R( a -- )
PUSHPC ( -- ) R( -- pc )
PUSH ( a -- ) R( -- a )
IMMED data:5 ( -- extend(data) )
JUMP rel:1 cc:3 drop:1 ( c a -- c? )
CALL rel:1 cc:3 drop:1 ( c a -- c? ) R( -- pc )
MERGE data:7 ( a -- ((a<<7)|data) )
cc=000: never (drop/drop2)
cc=001: ST0 == 0
cc=010: ST0 < 0
cc=011: ST0 > 0
cc=100: always
cc=101: ST0 != 0
cc=110: ST0 >= 0
cc=111: ST0 <= 0

53
doc/opcode-table.txt Normal file
View File

@ -0,0 +1,53 @@
opcode | mnem. | SP | RP | T | N | R | PC |cycles
---------|-----------|------|--------|-------|--------|--------|--------|------
0000000X | NOP | SP | RETURN | T | N | RETURN | RETURN | 1
0000001X | RSHIFT | SP-1 | RETURN | expr. | [SP-1] | RETURN | RETURN | 1
0000010X | ADD | SP-1 | RETURN | expr. | [SP-1] | RETURN | RETURN | 1
0000011X | SUB | SP-1 | RETURN | expr. | [SP-1] | RETURN | RETURN | 1
00001XXX | LOGIC | SP-1 | RETURN | expr. | [SP-1] | RETURN | RETURN | 1
0001000X | SWAP | SP | RETURN | N | T | RETURN | RETURN | 1
0001001X | DUP | SP+1 | RETURN | T | T | RETURN | RETURN | 1
00010100 | OVER | SP+1 | RP | N | T | R | PC+1 | 1
00010101 | RDUP | SP | RP+1 | T | N | R | PC+1 | 1
00010110 | NLZ | SP | RP | expr. | N | R | PC+1 | 1
00010111 | MULT | SP | RP | expr. | expr. | R | PC+1 | 2
0001100X | LOAD | SP | RP | expr. | N | R | PC+1 | 1*
0001101X | STORE | SP-2 | RP | expr. | [SP-2] | R | PC+1 | 1*
00011100 | RDROP | SP | RP-1 | T | N | [RP-1] | PC+1 | 1
00011101 | POP | SP+1 | RP-1 | R | T | [RP-1] | PC+1 | 1
00011110 | PUSHPC | SP | RP+1 | T | N | PC+1 | PC+1 | 1
00011111 | PUSH | SP-1 | RP+1 | N | [SP-1] | T | PC+1 | 1
001XXXXX | IMMED | SP+1 | RP | expr. | T | R | PC+1 | 1
010XXXX0 | JUMP | SP-1 | RP | N | [SP-1] | R | TARGET | 1*
010XXXX1 | JUMP DROP | SP-2 | RP | expr. | [SP-2] | R | TARGET | 1*
011XXXX0 | CALL | SP-1 | CALL | N | [SP-1] | CALL | TARGET | 1*
011XXXX1 | CALL DROP | SP-2 | CALL | expr. | [SP-2] | CALL | TARGET | 1*
1XXXXXXX | MERGE | SP | RP | expr. | N | R | PC+1 | 1
RP:RETURN = opcode[0] ? (RP-1) : RP
R:RETURN = opcode[0] ? [RP-1] : R
PC:RETURN = opcode[0] ? R : (PC+1)
RP:CALL = cond(N) ? (RP+1) : RP
R:CALL = cond(N) ? (PC+1) : R
PC:TARGET = cond(N) ? (rel ? (PC+1+T) : T) : (PC+1)
(*) External device can insert additional wait states.
opcode | T:expr. | N:expr. |
-------|---------|----------|
RSHIFT | N >> T | --- |
ADD | N + T | --- |
SUB | N - T | --- |
NIP | T | --- |
AND | N & T | --- |
OR | N | T | --- |
XOR | N ^ T | --- |
NLZ | nlz(T) | --- |
MULT | msw <- N*T -> lsw |
LOAD | data in | --- |
STORE | [SP-1] | --- |
IMMED | immed | --- |
J DROP | [SP-1] | --- |
C DROP | [SP-1] | --- |
MERGE | T:merge | --- |

72
fifo.v Normal file
View File

@ -0,0 +1,72 @@
module FIFO (CLK_I, RST_I, DAT_I, DAT_O, QUEUE_I, DEQUEUE_I, EMPTY_O, FULL_O);
// entries = 2**QUEUE_SIZE
parameter QUEUE_SIZE = 2;
parameter DATA_WIDTH = 32;
// If true, queue and dequeue can be simultaneous
// on empty and full queues. Otherwise data must
// pass through the queue first, costing one cycle.
parameter WRITE_THROUGH = 0;
input CLK_I, RST_I, QUEUE_I, DEQUEUE_I;
input [DATA_WIDTH-1:0] DAT_I;
output [DATA_WIDTH-1:0] DAT_O;
output EMPTY_O, FULL_O;
reg [QUEUE_SIZE-1:0] HEAD = 0;
reg [QUEUE_SIZE-1:0] TAIL = 0;
wire [QUEUE_SIZE-1:0] NEXT_HEAD = HEAD + 1;
generate
if (WRITE_THROUGH == 0)
begin : force_queue
assign EMPTY_O = (HEAD == TAIL);
assign FULL_O = (NEXT_HEAD == TAIL);
end
else
begin : write_through
assign EMPTY_O = (HEAD == TAIL) & ~QUEUE_I;
assign FULL_O = (NEXT_HEAD == TAIL) & ~DEQUEUE_I;
end
endgenerate
always @(posedge CLK_I)
begin
if (RST_I)
begin
HEAD <= 0;
TAIL <= 0;
end
else
begin
if (QUEUE_I & ~FULL_O)
HEAD <= HEAD + 1;
if (DEQUEUE_I & ~EMPTY_O)
TAIL <= TAIL + 1;
end
end
// synthesis attribute ram_style of QUEUE is distributed;
reg [DATA_WIDTH-1:0] QUEUE[0:(1<<QUEUE_SIZE)-1];
always @(posedge CLK_I)
begin
if (QUEUE_I & ~FULL_O)
QUEUE[HEAD] <= DAT_I;
end
generate
if (WRITE_THROUGH == 0)
begin : force_queue_out
assign DAT_O = QUEUE[TAIL];
end
else
begin : write_through_out
assign DAT_O = (HEAD == TAIL) ? DAT_I : QUEUE[TAIL];
end
endgenerate
endmodule

173
irq_ctl.v Normal file
View File

@ -0,0 +1,173 @@
// synthesis attribute slice_utilization_ratio of IRQ_CTL is 7;
module IRQ_CTL (CLK_I, RST_I, IRQ_O, IADR_O, IACK_I,
ADR_I, DAT_I, DAT_O, STB_I, WE_I, SEL_I, ACK_O,
IRQ7, IRQ6, IRQ5, IRQ4, IRQ3, IRQ2, IRQ1, IRQ0);
// Address layout (bytes):
// 0x00..0x03 IADR0
// 0x04..0x07 IADR1
// 0x08..0x0B IADR2
// 0x0C..0x0F IADR3
// 0x10..0x13 IADR4
// 0x14..0x17 IADR5
// 0x18..0x1B IADR6
// 0x1C..0x1F IADR7
// 0x20 polarity[irq7..irq0]
// 0x21 trigger[irq7..irq0]
// 0x22 mask[irq7..irq0]
// 0x23 status[irq7..irq0]
// 0x24..0x3F reserved (0)
// Triggers
`define TR_LEVEL 1'b0
`define TR_EDGE 1'b1
// Polarities
`define PL_LOW 1'b0
`define PL_HIGH 1'b1
`define PE_FALLING 1'b0
`define PE_RISING 1'b1
input CLK_I, RST_I, IACK_I, STB_I, WE_I;
output IRQ_O, ACK_O;
output [31:0] IADR_O;
input [3:0] ADR_I;
input [31:0] DAT_I;
output [31:0] DAT_O;
input [3:0] SEL_I;
input IRQ7, IRQ6, IRQ5, IRQ4;
input IRQ3, IRQ2, IRQ1, IRQ0;
// synthesis attribute ram_style of IADR is distributed;
reg [31:0] IADR[0:7];
reg [7:0] TRIGGER = 8'h00;
reg [7:0] POLARITY = 8'hFF;
reg [7:0] MASK = 8'h00;
reg [7:0] STATUS = 8'h00;
reg [7:0] PREV_IRQS = 8'h00;
reg [7:0] DELIVERED = 8'h00;
wire [7:0] PRIO_MASK = ~( DELIVERED | (DELIVERED<<1) | (DELIVERED<<2) | (DELIVERED<<3) |
(DELIVERED<<4) | (DELIVERED<<5) | (DELIVERED<<6) | (DELIVERED<<7) );
wire [7:0] IRQS = { IRQ7, IRQ6, IRQ5, IRQ4, IRQ3, IRQ2, IRQ1, IRQ0 };
wire [7:0] MASKED_IRQS = STATUS & MASK & PRIO_MASK;
// synthesis attribute priority_extract of HIGHEST_IRQ is force;
reg [2:0] HIGHEST_IRQ;
always @*
begin
if (MASKED_IRQS[0]) HIGHEST_IRQ <= 3'b000;
else if (MASKED_IRQS[1]) HIGHEST_IRQ <= 3'b001;
else if (MASKED_IRQS[2]) HIGHEST_IRQ <= 3'b010;
else if (MASKED_IRQS[3]) HIGHEST_IRQ <= 3'b011;
else if (MASKED_IRQS[4]) HIGHEST_IRQ <= 3'b100;
else if (MASKED_IRQS[5]) HIGHEST_IRQ <= 3'b101;
else if (MASKED_IRQS[6]) HIGHEST_IRQ <= 3'b110;
else if (MASKED_IRQS[7]) HIGHEST_IRQ <= 3'b111;
else HIGHEST_IRQ <= 3'bXXX;
end
// synthesis attribute decoder_extract of HIGHEST_BIT is yes;
reg [7:0] HIGHEST_BIT;
always @*
begin
case (HIGHEST_IRQ)
3'b000: HIGHEST_BIT <= 8'b00000001;
3'b001: HIGHEST_BIT <= 8'b00000010;
3'b010: HIGHEST_BIT <= 8'b00000100;
3'b011: HIGHEST_BIT <= 8'b00001000;
3'b100: HIGHEST_BIT <= 8'b00010000;
3'b101: HIGHEST_BIT <= 8'b00100000;
3'b110: HIGHEST_BIT <= 8'b01000000;
default: HIGHEST_BIT <= 8'b10000000;
endcase
end
wire [7:0] CLEAR_IRQS =
(STB_I & WE_I & (ADR_I == 4'b1000) & SEL_I[0]) ? DAT_I[7:0] : 8'h00;
// T[x] P[x] PIRQ[x] IRQ[x] | triggered
// 0 0 0 0 | 1
// 0 0 0 1 | 0
// 0 0 1 0 | 1
// 0 0 1 1 | 0
//
// 0 1 0 0 | 0
// 0 1 0 1 | 1
// 0 1 1 0 | 0
// 0 1 1 1 | 1
//
// 1 0 0 0 | 0
// 1 0 0 1 | 0
// 1 0 1 0 | 1
// 1 0 1 1 | 0
//
// 1 1 0 0 | 0
// 1 1 0 1 | 1
// 1 1 1 0 | 0
// 1 1 1 1 | 0
wire [7:0] TRIGGERED_IRQS;
generate
genvar i;
for (i=0; i<=7; i=i+1)
begin : trigger_luts
LUT4 #(
.INIT(16'b0010_0100_1010_0101)
) TriggerLUT (
.I3(TRIGGER[i]),
.I2(POLARITY[i]),
.I1(PREV_IRQS[i]),
.I0(IRQS[i]),
.O(TRIGGERED_IRQS[i])
);
end
endgenerate
always @(posedge CLK_I)
begin
if (RST_I)
begin
TRIGGER <= 8'h00;
POLARITY <= 8'hFF;
MASK <= 8'h00;
STATUS <= 8'h00;
PREV_IRQS <= 8'h00;
DELIVERED <= 8'h00;
end
else
begin
STATUS <= (STATUS & ~CLEAR_IRQS) | TRIGGERED_IRQS;
PREV_IRQS <= IRQS;
if (IACK_I)
DELIVERED <= (DELIVERED & ~CLEAR_IRQS) | HIGHEST_BIT;
else
DELIVERED <= (DELIVERED & ~CLEAR_IRQS);
if (STB_I & WE_I & ~ADR_I[3])
IADR[ADR_I[2:0]] <= DAT_I;
if (STB_I & WE_I & (ADR_I == 4'b1000))
begin
if (SEL_I[3]) TRIGGER <= DAT_I[31:24];
if (SEL_I[2]) POLARITY <= DAT_I[23:16];
if (SEL_I[1]) MASK <= DAT_I[15: 8];
end
end
end
wire [31:0] IADR_DATA, TPMS_DATA;
assign IADR_O = IADR[HIGHEST_IRQ];
assign IADR_DATA = IADR[ADR_I[2:0]];
assign IRQ_O = |MASKED_IRQS;
assign ACK_O = STB_I;
assign TPMS_DATA = { TRIGGER, POLARITY, MASK, STATUS };
assign DAT_O = ~ADR_I[3] ? IADR_DATA : TPMS_DATA;
endmodule

50
nlz.v Normal file
View File

@ -0,0 +1,50 @@
// Computes the number of leading zeros in the input. Result is 0...32.
module NLZ (IN, OUT);
input [31:0] IN;
output [5:0] OUT;
wire [7:0] PARTS = {
|IN[31:28], |IN[27:24], |IN[23:20], |IN[19:16],
|IN[15:12], |IN[11: 8], |IN[ 7: 4], |IN[ 3: 0]
};
// synthesis attribute priority_extract of LEADING_PARTS is force;
reg [2:0] LEADING_PARTS;
reg [1:0] LEADING_BITS;
reg [3:0] PART;
always @*
begin
if (PARTS[7]) LEADING_PARTS <= 3'b000;
else if (PARTS[6]) LEADING_PARTS <= 3'b001;
else if (PARTS[5]) LEADING_PARTS <= 3'b010;
else if (PARTS[4]) LEADING_PARTS <= 3'b011;
else if (PARTS[3]) LEADING_PARTS <= 3'b100;
else if (PARTS[2]) LEADING_PARTS <= 3'b101;
else if (PARTS[1]) LEADING_PARTS <= 3'b110;
else if (PARTS[0]) LEADING_PARTS <= 3'b111;
else LEADING_PARTS <= 3'bXXX;
case (LEADING_PARTS)
3'b000: PART <= IN[31:28];
3'b001: PART <= IN[27:24];
3'b010: PART <= IN[23:20];
3'b011: PART <= IN[19:16];
3'b100: PART <= IN[15:12];
3'b101: PART <= IN[11: 8];
3'b110: PART <= IN[ 7: 4];
3'b111: PART <= IN[ 3: 0];
endcase
casex (PART)
4'b1XXX: LEADING_BITS <= 2'b00;
4'b01XX: LEADING_BITS <= 2'b01;
4'b001X: LEADING_BITS <= 2'b10;
4'b000X: LEADING_BITS <= 2'b11;
endcase
end
assign OUT = (~|PARTS) ? 6'd32 : { LEADING_PARTS, LEADING_BITS };
endmodule

87
program.v Normal file
View File

@ -0,0 +1,87 @@
module ProgramRAM (CLK_I,RST_I,INST_ADR_I,INST_DAT_O,INST_STB_I,INST_ACK_O,
DATA_ADR_I,DATA_DAT_I,DATA_DAT_O,DATA_STB_I,DATA_WE_I,DATA_SEL_I,DATA_ACK_O);
input CLK_I;
input RST_I;
input [12:0] INST_ADR_I;
output [7:0] INST_DAT_O;
input INST_STB_I;
output INST_ACK_O;
input [12:2] DATA_ADR_I;
input [31:0] DATA_DAT_I;
output [31:0] DATA_DAT_O;
input DATA_STB_I;
input DATA_WE_I;
input [3:0] DATA_SEL_I;
output DATA_ACK_O;
// synthesis attribute ram_style of RAM is block;
reg [31:0] RAM[0:2047];
reg [12:2] READ_INST;
reg [12:2] READ_DATA;
reg [7:0] INST_DAT_O;
reg INST_ACK_O;
wire [31:0] DATA_DAT_O;
reg DATA_ACK_O;
initial
begin
$readmemh("program.data", RAM, 0, 2047);
end
reg [12:2] INST_PRED_ADR;
wire WORD_ACCESS = &DATA_SEL_I;
reg DATA_READ = 1'b0;
reg [31:0] RMW_DATA;
always @(posedge CLK_I)
begin
if (DATA_STB_I & DATA_WE_I & (WORD_ACCESS | DATA_READ))
RAM[DATA_ADR_I] <= WORD_ACCESS ? DATA_DAT_I : RMW_DATA;
READ_DATA <= DATA_ADR_I[12:2];
READ_INST <= INST_PRED_ADR[12:2];
DATA_READ <= DATA_STB_I & ~DATA_ACK_O;
end
wire [31:0] INST_DAT32_O;
assign INST_DAT32_O = RAM[READ_INST];
assign DATA_DAT_O = RAM[READ_DATA];
wire [12:0] NEXT_INST_ADR = INST_ADR_I + 13'd1;
wire PRED_CORRECT = (READ_INST == INST_ADR_I[12:2]);
wire READ_DONE = DATA_READ;
wire WRITE_DONE = WORD_ACCESS | DATA_READ;
always @*
begin
case (INST_ADR_I[1:0])
2'b00: INST_DAT_O <= INST_DAT32_O[31:24];
2'b01: INST_DAT_O <= INST_DAT32_O[23:16];
2'b10: INST_DAT_O <= INST_DAT32_O[15:8];
2'b11: INST_DAT_O <= INST_DAT32_O[7:0];
endcase
if (~INST_STB_I | PRED_CORRECT)
INST_PRED_ADR <= NEXT_INST_ADR[12:2];
else // incorrectly predicted, forces wait state
INST_PRED_ADR <= INST_ADR_I[12:2];
INST_ACK_O <= INST_STB_I & PRED_CORRECT;
DATA_ACK_O <= DATA_STB_I & (DATA_WE_I ? WRITE_DONE : READ_DONE);
RMW_DATA[31:24] <= DATA_SEL_I[3] ? DATA_DAT_I[31:24] : DATA_DAT_O[31:24];
RMW_DATA[23:16] <= DATA_SEL_I[2] ? DATA_DAT_I[23:16] : DATA_DAT_O[23:16];
RMW_DATA[15: 8] <= DATA_SEL_I[1] ? DATA_DAT_I[15: 8] : DATA_DAT_O[15: 8];
RMW_DATA[ 7: 0] <= DATA_SEL_I[0] ? DATA_DAT_I[ 7: 0] : DATA_DAT_O[ 7: 0];
end
endmodule

130
rs232.v Normal file
View File

@ -0,0 +1,130 @@
// synthesis attribute slice_utilization_ratio of RS232 is 7;
module RS232 (CLK_I, RST_I, DAT_I, DAT_O, STB_I, WE_I, SEL_I, ACK_O,
TX_EMPTY, TX_FULL, RX_EMPTY, RX_FULL, RX, TX);
//parameter CLOCK_DIV = 5207; // 9600 bps @ 50 MHz CLK_I
parameter CLOCK_DIV = 434; // 115200 bps @ 50 MHz CLK_I
input CLK_I;
input RST_I;
input [31:0] DAT_I;
output [31:0] DAT_O;
input STB_I;
input WE_I;
input [3:0] SEL_I;
output ACK_O;
output TX_EMPTY, TX_FULL;
output RX_EMPTY, RX_FULL;
input RX;
output TX;
reg TX = 1'b1;
`define IDLE 4'd0
`define START 4'd1
`define BIT0 4'd2
`define BIT1 4'd3
`define BIT2 4'd4
`define BIT3 4'd5
`define BIT4 4'd6
`define BIT5 4'd7
`define BIT6 4'd8
`define BIT7 4'd9
`define STOP 4'd10
reg [17:0] TX_COUNTER;
reg [9:0] TX_BUFFER;
reg [3:0] TX_STATE = `IDLE;
reg [18:0] RX_COUNTER;
reg [6:0] RX_BUFFER;
reg [3:0] RX_STATE = `IDLE;
wire [7:0] TX_DATA;
wire [7:0] RX_DATA;
FIFO #(
.QUEUE_SIZE(4), // 16 characters
.DATA_WIDTH(8)
) TransmitFIFO (
.CLK_I(CLK_I),
.RST_I(RST_I),
.DAT_I(DAT_I[7:0]),
.DAT_O(TX_DATA),
.QUEUE_I(ACK_O & WE_I & SEL_I[0]),
.DEQUEUE_I(TX_STATE == `IDLE),
.FULL_O(TX_FULL),
.EMPTY_O(TX_EMPTY)
);
FIFO #(
.QUEUE_SIZE(4), // 16 characters
.DATA_WIDTH(8)
) ReceiveFIFO (
.CLK_I(CLK_I),
.RST_I(RST_I),
.DAT_I({ RX, RX_BUFFER }),
.DAT_O(RX_DATA),
.QUEUE_I((RX_STATE == `BIT7) & (RX_COUNTER == 0)),
.DEQUEUE_I(ACK_O & ~WE_I & SEL_I[0]),
.FULL_O(RX_FULL),
.EMPTY_O(RX_EMPTY)
);
assign DAT_O = { 22'h000000, TX_FULL, ~RX_EMPTY, RX_DATA };
assign ACK_O = STB_I;
always @(posedge CLK_I)
begin
if (TX_STATE == `IDLE)
begin
TX <= 1'b1;
TX_COUNTER <= (CLOCK_DIV - 1);
if (~TX_EMPTY)
begin
TX_BUFFER <= { 1'b1, 1'b1, TX_DATA, 1'b0 };
TX_STATE <= `START;
end
end
else if (~|TX_COUNTER)
begin
TX <= TX_BUFFER[0];
TX_BUFFER <= TX_BUFFER >> 1;
TX_COUNTER <= (CLOCK_DIV - 1);
if (TX_STATE == `STOP)
TX_STATE <= `IDLE;
else
TX_STATE <= TX_STATE + 1;
end
else
TX_COUNTER <= TX_COUNTER - 1;
end
always @(posedge CLK_I)
begin
if ((RX_STATE == `IDLE) | RST_I)
begin
RX_COUNTER <= (CLOCK_DIV / 2);
if (RX == 1'b0)
RX_STATE <= `START;
else
RX_STATE <= `IDLE;
end
else if (RX_COUNTER == 0)
begin
RX_BUFFER <= { RX, RX_BUFFER[6:1] };
RX_COUNTER <= (CLOCK_DIV - 1);
if (RX_STATE == `STOP)
RX_STATE <= `IDLE;
else
RX_STATE <= RX_STATE + 1;
end
else
RX_COUNTER <= RX_COUNTER - 1;
end
endmodule

64
seven-seg.v Normal file
View File

@ -0,0 +1,64 @@
module SevenSeg (CLKIN, INPUT, DP, SEG, ANODE);
input CLKIN;
input [15:0] INPUT;
input [3:0] DP;
output [7:0] SEG;
output [3:0] ANODE;
reg [7:0] SEG = 1'b1;
reg [3:0] ANODE = 4'b1111;
reg [18:0] CNT;
wire [1:0] SEG_SEL = CNT[18:17];
reg [3:0] SEG_BIN;
always @*
begin
case (SEG_SEL)
2'b00: SEG_BIN <= INPUT[15:12];
2'b01: SEG_BIN <= INPUT[11:8];
2'b10: SEG_BIN <= INPUT[7:4];
2'b11: SEG_BIN <= INPUT[3:0];
endcase
end
always @(posedge CLKIN)
begin
CNT <= CNT + 1;
casex (SEG_SEL)
2'b00: ANODE <= 4'b0111;
2'b01: ANODE <= 4'b1011;
2'b10: ANODE <= 4'b1101;
2'b11: ANODE <= 4'b1110;
endcase
case (SEG_BIN)
4'h0: SEG[7:1] <= 8'b0000001;
4'h1: SEG[7:1] <= 8'b1001111;
4'h2: SEG[7:1] <= 8'b0010010;
4'h3: SEG[7:1] <= 8'b0000110;
4'h4: SEG[7:1] <= 8'b1001100;
4'h5: SEG[7:1] <= 8'b0100100;
4'h6: SEG[7:1] <= 8'b0100000;
4'h7: SEG[7:1] <= 8'b0001111;
4'h8: SEG[7:1] <= 8'b0000000;
4'h9: SEG[7:1] <= 8'b0000100;
4'hA: SEG[7:1] <= 8'b0001000;
4'hB: SEG[7:1] <= 8'b1100000;
4'hC: SEG[7:1] <= 8'b0110001;
4'hD: SEG[7:1] <= 8'b1000010;
4'hE: SEG[7:1] <= 8'b0110000;
4'hF: SEG[7:1] <= 8'b0111000;
endcase
case (SEG_SEL)
2'b00: SEG[0] <= ~DP[3];
2'b01: SEG[0] <= ~DP[2];
2'b10: SEG[0] <= ~DP[1];
2'b11: SEG[0] <= ~DP[0];
endcase
end
endmodule

125
sram.v Normal file
View File

@ -0,0 +1,125 @@
module WB_SRAM (CLK_I, RST_I, ADR_I, DAT_I, DAT_O, STB_I, WE_I, SEL_I, ACK_O,
SRAM_IO1, SRAM_CE1, SRAM_UB1, SRAM_LB1, SRAM_ADR, SRAM_WE, SRAM_OE,
SRAM_IO2, SRAM_CE2, SRAM_UB2, SRAM_LB2);
// Wishbone signals
input CLK_I, RST_I, STB_I, WE_I;
input [17:0] ADR_I;
input [31:0] DAT_I;
output [31:0] DAT_O;
input [3:0] SEL_I;
output ACK_O;
// SRAM signals
output [17:0] SRAM_ADR;
output SRAM_WE, SRAM_OE;
inout [15:0] SRAM_IO1, SRAM_IO2;
output SRAM_CE1, SRAM_CE2;
output SRAM_UB1, SRAM_UB2;
output SRAM_LB1, SRAM_LB2;
assign SRAM_CE1 = 1'b0;
assign SRAM_CE2 = 1'b0;
// synthesis attribute iob of SRAM_ADR_O is true;
// synthesis attribute iob of SRAM_DAT_O is true;
// synthesis attribute iob of SRAM_SEL_O is true;
// synthesis attribute iob of SRAM_OE_O is true;
// synthesis attribute iob of SRAM_WE_O is true;
// synthesis attribute iob of HIGH_Z is true;
reg [17:0] SRAM_ADR_O;
reg [31:0] SRAM_DAT_O;
wire [31:0] SRAM_DAT_I;
reg [3:0] SRAM_SEL_O;
reg HIGH_Z = 1'b1;
assign SRAM_UB1 = SRAM_SEL_O[3];
assign SRAM_LB1 = SRAM_SEL_O[2];
assign SRAM_UB2 = SRAM_SEL_O[1];
assign SRAM_LB2 = SRAM_SEL_O[0];
assign SRAM_ADR = SRAM_ADR_O;
assign SRAM_IO1 = HIGH_Z ? 16'dZ : SRAM_DAT_O[31:16];
assign SRAM_IO2 = HIGH_Z ? 16'dZ : SRAM_DAT_O[15: 0];
assign SRAM_DAT_I = { SRAM_IO1, SRAM_IO2 };
`define SRS_IDLE 2'd0
`define SRS_READ 2'd1
`define SRS_WRITE 2'd2
reg [1:0] STATE = `SRS_IDLE;
reg SRAM_WE = 1'b1;
reg SRAM_OE = 1'b1;
reg ACK_O = 1'b0;
reg [31:0] DAT_O;
always @(negedge CLK_I)
SRAM_WE <= ~(STATE == `SRS_WRITE);
always @(posedge CLK_I)
begin
DAT_O <= SRAM_DAT_I;
if (RST_I)
begin
SRAM_OE <= 1'b1;
HIGH_Z <= 1'b1;
ACK_O <= 1'b0;
STATE <= `SRS_IDLE;
end
else
begin
case (STATE)
`SRS_IDLE:
begin
SRAM_ADR_O <= ADR_I;
SRAM_DAT_O <= DAT_I;
SRAM_SEL_O <= ~SEL_I;
ACK_O <= 1'b0;
if (STB_I)
begin
if (WE_I)
begin
SRAM_OE <= 1'b1;
HIGH_Z <= 1'b0;
STATE <= `SRS_WRITE;
end
else
begin
SRAM_OE <= 1'b0;
HIGH_Z <= 1'b1;
STATE <= `SRS_READ;
end
end
else
begin
SRAM_OE <= 1'b1;
HIGH_Z <= 1'b1;
STATE <= `SRS_IDLE;
end
end
`SRS_READ:
begin
SRAM_OE <= 1'b1;
HIGH_Z <= 1'b1;
ACK_O <= 1'b1;
STATE <= `SRS_IDLE;
end
`SRS_WRITE:
begin
SRAM_OE <= 1'b1;
HIGH_Z <= 1'b1;
ACK_O <= 1'b1;
STATE <= `SRS_IDLE;
end
default:
STATE <= `SRS_IDLE;
endcase
end
end
endmodule

34
stack.v Normal file
View File

@ -0,0 +1,34 @@
module StackRAM (CLKIN, ADDR_A, WE_A, IN_A, OUT_A, ADDR_B, OUT_B);
parameter ADDR_WIDTH = 9;
parameter DATA_WIDTH = 32;
input CLKIN;
input [ADDR_WIDTH-1:0] ADDR_A;
input WE_A;
input [DATA_WIDTH-1:0] IN_A;
output [DATA_WIDTH-1:0] OUT_A;
input [ADDR_WIDTH-1:0] ADDR_B;
output [DATA_WIDTH-1:0] OUT_B;
reg [DATA_WIDTH-1:0] RAM[0:(1<<ADDR_WIDTH)-1];
reg [ADDR_WIDTH-1:0] READ_A;
reg [ADDR_WIDTH-1:0] READ_B;
assign OUT_A = RAM[READ_A];
assign OUT_B = RAM[READ_B];
always @(posedge CLKIN)
begin
if (WE_A)
RAM[ADDR_A] <= IN_A;
READ_A <= ADDR_A;
end
always @(negedge CLKIN)
begin
READ_B <= ADDR_B;
end
endmodule

61
timer.v Normal file
View File

@ -0,0 +1,61 @@
module TIMER (CLK_I,RST_I,ADR_I,DAT_I,DAT_O,STB_I,WE_I,ACK_O,IRQ_O);
parameter INIT_RELOAD = 0;
input CLK_I, RST_I, STB_I, WE_I;
input [1:0] ADR_I;
input [31:0] DAT_I;
output [31:0] DAT_O;
output ACK_O, IRQ_O;
reg [31:0] RELOAD = INIT_RELOAD;
reg [31:0] COUNTER = INIT_RELOAD;
reg [63:0] TIMEBASE = 64'd0;
wire ZERO = ~|COUNTER;
assign ACK_O = STB_I;
assign IRQ_O = ZERO & ~RST_I;
reg [31:0] DAT_O;
always @*
begin
case (ADR_I)
2'd0: DAT_O <= COUNTER;
2'd1: DAT_O <= RELOAD;
2'd2: DAT_O <= TIMEBASE[63:32];
2'd3: DAT_O <= TIMEBASE[31:0];
endcase
end
always @(posedge CLK_I)
begin
if (RST_I)
begin
RELOAD <= INIT_RELOAD;
COUNTER <= INIT_RELOAD;
TIMEBASE <= 64'd0;
end
else
begin
if (STB_I & WE_I & ~ADR_I[1] & ~ADR_I[0])
COUNTER <= DAT_I;
else if (ZERO | RST_I)
COUNTER <= RELOAD;
else
COUNTER <= COUNTER - 1;
if (STB_I & WE_I & ~ADR_I[1] & ADR_I[0])
RELOAD <= DAT_I;
if (STB_I & WE_I & ADR_I[1] & ~ADR_I[0])
TIMEBASE <= { DAT_I, 32'd0 };
else if (STB_I & WE_I & ADR_I[1] & ADR_I[0])
TIMEBASE <= { TIMEBASE[63:32], DAT_I };
else
TIMEBASE <= TIMEBASE + 1;
end
end
endmodule

13
toplevel.prj Normal file
View File

@ -0,0 +1,13 @@
verilog work cpu.v
verilog work stack.v
verilog work nlz.v
verilog work program.v
verilog work toplevel.v
verilog work seven-seg.v
verilog work rs232.v
verilog work fifo.v
verilog work sram.v
verilog work irq_ctl.v
verilog work arbiter.v
verilog work dma.v
verilog work timer.v

209
toplevel.ucf Normal file
View File

@ -0,0 +1,209 @@
#######################################################################
# #
# Pin Description for the Spartan-3 Starter Kit Board #
# #
#######################################################################
#
# 50 MHz on-board clock source
#
NET "CLKIN" LOC = T9;
NET "CLKIN" TNM_NET = "CLKIN";
TIMESPEC "TS_CLKIN" = PERIOD "CLKIN" 20 ns HIGH 50%;
OFFSET = OUT 20 ns AFTER "CLKIN";
#
# On-board Static RAM
#
TIMEGRP "SRAM_ADR" = PADS("SRAM_ADR<*>");
TIMEGRP "SRAM_IO" = PADS("SRAM_IO?<*>");
TIMEGRP "SRAM_CE" = PADS("SRAM_CE?");
TIMEGRP "SRAM_BE" = PADS("SRAM_UB?") PADS("SRAM_LB?");
TIMEGRP "SRAM_WE" = PADS("SRAM_WE");
TIMEGRP "SRAM_OE" = PADS("SRAM_OE");
TIMEGRP "SRAM_ADR" OFFSET = OUT 6.0 ns AFTER "CLKIN";
TIMEGRP "SRAM_IO" OFFSET = IN 15.0 ns VALID 20 ns AFTER "CLKIN";
#TIMEGRP "SRAM_CE" OFFSET = OUT 2.0 ns AFTER "CLKIN";
TIMEGRP "SRAM_BE" OFFSET = OUT 8.0 ns AFTER "CLKIN";
TIMEGRP "SRAM_OE" OFFSET = OUT 8.0 ns AFTER "CLKIN";
TIMEGRP "SRAM_IO" OFFSET = OUT 14.0 ns AFTER "CLKIN";
TIMEGRP "SRAM_WE" OFFSET = OUT 18.0 ns AFTER "CLKIN";
NET "SRAM_ADR<*>" DRIVE = 4;
NET "SRAM_IO1<*>" DRIVE = 4;
NET "SRAM_IO2<*>" DRIVE = 4;
NET "SRAM_CE?" DRIVE = 4;
NET "SRAM_UB?" DRIVE = 4;
NET "SRAM_LB?" DRIVE = 4;
NET "SRAM_WE" DRIVE = 4;
NET "SRAM_OE" DRIVE = 4;
NET "SRAM_ADR<*>" FAST;
NET "SRAM_IO1<*>" FAST;
NET "SRAM_IO2<*>" FAST;
NET "SRAM_CE?" FAST;
NET "SRAM_UB?" FAST;
NET "SRAM_LB?" FAST;
NET "SRAM_WE" FAST;
NET "SRAM_OE" FAST;
NET "SRAM_ADR<17>" LOC = L3; # Address to SRAM bus
NET "SRAM_ADR<16>" LOC = K5;
NET "SRAM_ADR<15>" LOC = K3;
NET "SRAM_ADR<14>" LOC = J3;
NET "SRAM_ADR<13>" LOC = J4;
NET "SRAM_ADR<12>" LOC = H4;
NET "SRAM_ADR<11>" LOC = H3;
NET "SRAM_ADR<10>" LOC = G5;
NET "SRAM_ADR<9>" LOC = E4;
NET "SRAM_ADR<8>" LOC = E3;
NET "SRAM_ADR<7>" LOC = F4;
NET "SRAM_ADR<6>" LOC = F3;
NET "SRAM_ADR<5>" LOC = G4;
NET "SRAM_ADR<4>" LOC = L4;
NET "SRAM_ADR<3>" LOC = M3;
NET "SRAM_ADR<2>" LOC = M4;
NET "SRAM_ADR<1>" LOC = N3;
NET "SRAM_ADR<0>" LOC = L5;
NET "SRAM_IO1<15>" LOC = R1; # Data to and/or from SRAM #1
NET "SRAM_IO1<14>" LOC = P1;
NET "SRAM_IO1<13>" LOC = L2;
NET "SRAM_IO1<12>" LOC = J2;
NET "SRAM_IO1<11>" LOC = H1;
NET "SRAM_IO1<10>" LOC = F2;
NET "SRAM_IO1<9>" LOC = P8;
NET "SRAM_IO1<8>" LOC = D3;
NET "SRAM_IO1<7>" LOC = B1;
NET "SRAM_IO1<6>" LOC = C1;
NET "SRAM_IO1<5>" LOC = C2;
NET "SRAM_IO1<4>" LOC = R5;
NET "SRAM_IO1<3>" LOC = T5;
NET "SRAM_IO1<2>" LOC = R6;
NET "SRAM_IO1<1>" LOC = T8;
NET "SRAM_IO1<0>" LOC = N7;
NET "SRAM_CE1" LOC = P7; # Control signals to SRAM #1
NET "SRAM_UB1" LOC = T4;
NET "SRAM_LB1" LOC = P6;
NET "SRAM_IO2<15>" LOC = N1; # Data to/from SRAM #2
NET "SRAM_IO2<14>" LOC = M1;
NET "SRAM_IO2<13>" LOC = K2;
NET "SRAM_IO2<12>" LOC = C3;
NET "SRAM_IO2<11>" LOC = F5;
NET "SRAM_IO2<10>" LOC = G1;
NET "SRAM_IO2<9>" LOC = E2;
NET "SRAM_IO2<8>" LOC = D2;
NET "SRAM_IO2<7>" LOC = D1;
NET "SRAM_IO2<6>" LOC = E1;
NET "SRAM_IO2<5>" LOC = G2;
NET "SRAM_IO2<4>" LOC = J1;
NET "SRAM_IO2<3>" LOC = K1;
NET "SRAM_IO2<2>" LOC = M2;
NET "SRAM_IO2<1>" LOC = N2;
NET "SRAM_IO2<0>" LOC = P2;
NET "SRAM_CE2" LOC = N5; # Control signals to SRAM #2
NET "SRAM_UB2" LOC = R4;
NET "SRAM_LB2" LOC = P5;
NET "SRAM_WE" LOC = G3; # Control signals to SRAMs #1 and #2
NET "SRAM_OE" LOC = K4;
#
# RS-232 Serial Port (w/ DB-9)
#
TIMEGRP "RS232" = PADS("RX_A") PADS("TX_A");
NET "TX_A" DRIVE = 4;
NET "RX_A" LOC = T13; # serial input from V.24 driver
NET "TX_A" LOC = R13; # serial output to V.24 driver
#
# On-board LEDs
#
TIMEGRP "LEDS" = PADS("LED<*>");
NET "LED<*>" DRIVE = 4;
NET "LED<7>" LOC = P11; # signal to 8 LEDs (7 is leftmost)
NET "LED<6>" LOC = P12;
NET "LED<5>" LOC = N12;
NET "LED<4>" LOC = P13;
NET "LED<3>" LOC = N14;
NET "LED<2>" LOC = L12;
NET "LED<1>" LOC = P14;
NET "LED<0>" LOC = K12;
#
# On-board 7-segment LED displays
#
TIMEGRP "DISPLAY" = PADS("SEG<*>") PADS("ANODE<*>");
NET "SEG<*>" DRIVE = 4;
NET "ANODE<*>" DRIVE = 4;
NET "SEG<7>" LOC = E14; # (a) signal to 7-segment displays
NET "SEG<6>" LOC = G13; # (b)
NET "SEG<5>" LOC = N15; # (c) Note: active-low outputs
NET "SEG<4>" LOC = P15; # (d)
NET "SEG<3>" LOC = R16; # (e)
NET "SEG<2>" LOC = F13; # (f)
NET "SEG<1>" LOC = N16; # (g)
NET "SEG<0>" LOC = P16; # (dp)
NET "ANODE<3>" LOC = E13; # selector for SEG<> (3 is leftmost)
NET "ANODE<2>" LOC = F14;
NET "ANODE<1>" LOC = G14; # Note: active-low outputs
NET "ANODE<0>" LOC = D14;
#
# On-board slide switches
#
TIMEGRP "SWITCHES" = PADS("SWITCH<*>");
NET "SWITCH<7>" LOC = K13; # signal from slide switches (7 is leftmost)
NET "SWITCH<6>" LOC = K14;
NET "SWITCH<5>" LOC = J13;
NET "SWITCH<4>" LOC = J14;
NET "SWITCH<3>" LOC = H13;
NET "SWITCH<2>" LOC = H14;
NET "SWITCH<1>" LOC = G12;
NET "SWITCH<0>" LOC = F12;
#
# On-board pushbuttons
#
TIMEGRP "BUTTONS" = PADS("BTN<*>");
NET "BTN<3>" LOC = L14; # signal from push-buttons (3 is leftmost)
NET "BTN<2>" LOC = L13;
NET "BTN<1>" LOC = M14;
NET "BTN<0>" LOC = M13;
#
# On-board 3-bit VGA port
#
#TIMEGRP "VGA" = PADS("VGA_*");
#NET "VGA_*" DRIVE = 4;
#NET "VGA_RED" LOC = R12; # Output to 3-bit VGA display interface
#NET "VGA_GREEN" LOC = T12;
#NET "VGA_BLUE" LOC = R11;
#NET "VGA_H_SYNC" LOC = R9;
#NET "VGA_V_SYNC" LOC = T10;
#
# On-board PS/2 keyboard/mouse port
#
#TIMEGRP "PS2" = PADS("PS2_*");
#NET "PS2_CLK" DRIVE = 4;
#NET "PS2_DATA" LOC = M15; # Input from PS/2 keyboard/mouse port
#NET "PS2_CLK" LOC = M16;

416
toplevel.v Normal file
View File

@ -0,0 +1,416 @@
module TopLevel (CLKIN, BTN, SWITCH, LED, SEG, ANODE, RX_A, TX_A,
SRAM_IO1, SRAM_CE1, SRAM_UB1, SRAM_LB1, SRAM_ADR, SRAM_WE, SRAM_OE,
SRAM_IO2, SRAM_CE2, SRAM_UB2, SRAM_LB2);
input CLKIN;
input [3:0] BTN;
input [7:0] SWITCH;
output [7:0] LED;
output [7:0] SEG;
output [3:0] ANODE;
input RX_A;
output TX_A;
output [17:0] SRAM_ADR;
output SRAM_WE;
output SRAM_OE;
inout [15:0] SRAM_IO1;
output SRAM_CE1;
output SRAM_UB1;
output SRAM_LB1;
inout [15:0] SRAM_IO2;
output SRAM_CE2;
output SRAM_UB2;
output SRAM_LB2;
DCM #(
.CLKFX_DIVIDE(2), // Can be any integer from 1 to 32
.CLKFX_MULTIPLY(2), // Can be any integer from 2 to 32
.CLKIN_PERIOD(20.0), // Period of input clock
.STARTUP_WAIT("TRUE"), // Delay configuration DONE until DCM LOCK?
.CLKDV_DIVIDE(2.0), // [1.5,7.5]/0.5 or [8.0,16.0]/1.0
.CLKIN_DIVIDE_BY_2("FALSE"), // Enable CLKIN divide-by-two feature?
.CLKOUT_PHASE_SHIFT("NONE"), // NONE, FIXED or VARIABLE
.CLK_FEEDBACK("1X"), // Specify clock feedback of NONE, 1X or 2X
.PHASE_SHIFT(0) // Amount of fixed phase shift, -255 to 255
) DCM0 (
.CLK0(CLK0), // CLKOUT w/ 0 degree shift
// .CLK2X(CLK_100), // 2 * CLK0 frequency
// .CLKFX(CLKFX), // DCM CLK synthesis out (M/D)
.CLKIN(CLKIN), // Clock input (from IBUFG, BUFG or DCM)
.CLKFB(CLK0) // DCM clock feedback
);
wire CLK_O = CLK0;
reg RST_O = 1'b1;
wire [31:0] IADR_I;
wire [31:0] INST_ADR_O;
wire [7:0] INST_DAT_I;
wire [31:2] CPU_ADR_O;
wire [31:0] CPU_DAT_I;
wire [31:0] CPU_DAT_O;
wire [3:0] CPU_SEL_O;
wire CPU_ACK_I;
StackCPU MyStackCPU (
.CLK_I(CLK_O),
.RST_I(RST_O),
.IRQ_I(IRQ_I),
.IADR_I(IADR_I),
.IACK_O(IACK_O),
.INST_ADR_O(INST_ADR_O),
.INST_DAT_I(INST_DAT_I),
.INST_CYC_O(INST_CYC_O),
.INST_STB_O(INST_STB_O),
.INST_ACK_I(INST_ACK_I),
.DATA_ADR_O(CPU_ADR_O),
.DATA_DAT_I(CPU_DAT_I),
.DATA_DAT_O(CPU_DAT_O),
.DATA_CYC_O(CPU_CYC_O),
.DATA_STB_O(CPU_STB_O),
.DATA_WE_O(CPU_WE_O),
.DATA_SEL_O(CPU_SEL_O),
.DATA_ACK_I(CPU_ACK_I)
);
// Bus arbiter master signals
wire [31:2] DATA_ADR_O;
reg [31:0] DATA_DAT_I;
wire [31:0] DATA_DAT_O;
wire [3:0] DATA_SEL_O;
reg DATA_ACK_I;
`define SLV_PROGRAM_RAM 4'b0000
`define SLV_SRAM 4'b0001
`define SLV_IRQ_CTL 4'b0010
`define SLV_DMA_CTL 4'b0011
`define SLV_TIMER 4'b0100
`define SLV_LED_SWITCH 4'b0101
`define SLV_BTN_DISPLAY 4'b0110
`define SLV_RS232 4'b0111
`define SLV_UNUSED 4'b1111
reg [3:0] SLAVE;
always @*
begin
/*
casex (DATA_ADR_O)
30'bXXXX_XXXX_XX00_XXXX_XXXX_XXXX_XXXX_XX: SLAVE <= `SLV_PROGRAM_RAM;
30'bXXXX_XXXX_XX01_XXXX_XXXX_XXXX_XXXX_XX: SLAVE <= `SLV_SRAM;
30'bXXXX_XXXX_XX11_XXXX_XXXX_XXXX_X0XX_XX: SLAVE <= `SLV_IRQ_CTL;
30'bXXXX_XXXX_XX11_XXXX_XXXX_XXXX_X10X_XX: SLAVE <= `SLV_DMA_CTL;
30'bXXXX_XXXX_XX11_XXXX_XXXX_XXXX_X110_XX: SLAVE <= `SLV_TIMER;
30'bXXXX_XXXX_XX11_XXXX_XXXX_XXXX_X111_00: SLAVE <= `SLV_LED_SWITCH;
30'bXXXX_XXXX_XX11_XXXX_XXXX_XXXX_X111_01: SLAVE <= `SLV_BTN_DISPLAY;
30'bXXXX_XXXX_XX11_XXXX_XXXX_XXXX_X111_1X: SLAVE <= `SLV_RS232;
default: SLAVE <= `SLV_UNUSED;
endcase
*/
// Manually decode the above table, because xst appears to
// attempt to expand it rather than ignore the 'X' bits.
case (DATA_ADR_O[21:20])
2'b00: SLAVE <= `SLV_PROGRAM_RAM;
2'b01: SLAVE <= `SLV_SRAM;
2'b10: SLAVE <= `SLV_UNUSED;
2'b11:
casex (DATA_ADR_O[6:2])
5'b0XX_XX: SLAVE <= `SLV_IRQ_CTL;
5'b10X_XX: SLAVE <= `SLV_DMA_CTL;
5'b110_XX: SLAVE <= `SLV_TIMER;
5'b111_00: SLAVE <= `SLV_LED_SWITCH;
5'b111_01: SLAVE <= `SLV_BTN_DISPLAY;
5'b111_1X: SLAVE <= `SLV_RS232;
default: SLAVE <= `SLV_UNUSED;
endcase
endcase
end
wire [31:0] DMA_CTL_DAT_O;
wire [31:2] DMA_SRC_ADR_O;
wire [31:0] DMA_SRC_DAT_I;
wire [31:2] DMA_DST_ADR_O;
wire [31:0] DMA_DST_DAT_O;
WB_DMA DMAController (
.CLK_I(CLK_O),
.RST_I(RST_O),
.IRQ_O(DMA_IRQ_O),
.CTL_ADR_I(DATA_ADR_O[3:2]),
.CTL_DAT_I(DATA_DAT_O),
.CTL_DAT_O(DMA_CTL_DAT_O),
.CTL_STB_I(DATA_STB_O & (SLAVE == `SLV_DMA_CTL)),
.CTL_WE_I(DATA_WE_O),
.CTL_ACK_O(DMA_CTL_ACK_O),
.SRC_ADR_O(DMA_SRC_ADR_O),
.SRC_DAT_I(DMA_SRC_DAT_I),
.SRC_STB_O(DMA_SRC_STB_O),
.SRC_WE_O(DMA_SRC_WE_O),
.SRC_ACK_I(DMA_SRC_ACK_I),
.DST_ADR_O(DMA_DST_ADR_O),
.DST_DAT_O(DMA_DST_DAT_O),
.DST_STB_O(DMA_DST_STB_O),
.DST_WE_O(DMA_DST_WE_O),
.DST_ACK_I(DMA_DST_ACK_I)
);
wire [31:2] DMA_ADR_O;
wire [31:0] DMA_DAT_O;
wire [31:0] DMA_DAT_I;
wire [3:0] DMA_SEL_O;
wire DMA_ACK_I;
WB_ARB_2M DMAArbiter (
.CLK_I(CLK_O),
.RST_I(RST_O),
.M0_ADR_I(DMA_SRC_ADR_O),
.M0_DAT_I(32'd0),
.M0_DAT_O(DMA_SRC_DAT_I),
.M0_LOCK_I(1'b0),
.M0_CYC_I(DMA_SRC_STB_O),
.M0_STB_I(DMA_SRC_STB_O),
.M0_WE_I(DMA_SRC_WE_O),
.M0_SEL_I(4'b1111),
.M0_ACK_O(DMA_SRC_ACK_I),
.M1_ADR_I(DMA_DST_ADR_O),
.M1_DAT_I(DMA_DST_DAT_O),
.M1_LOCK_I(1'b0),
.M1_CYC_I(DMA_DST_STB_O),
.M1_STB_I(DMA_DST_STB_O),
.M1_WE_I(DMA_DST_WE_O),
.M1_SEL_I(4'b1111),
.M1_ACK_O(DMA_DST_ACK_I),
.S_ADR_O(DMA_ADR_O),
.S_DAT_O(DMA_DAT_O),
.S_DAT_I(DMA_DAT_I),
.S_CYC_O(DMA_CYC_O),
.S_STB_O(DMA_STB_O),
.S_WE_O(DMA_WE_O),
.S_SEL_O(DMA_SEL_O),
.S_ACK_I(DMA_ACK_I),
.S_ERR_I(1'b0),
.S_RTY_I(1'b0)
);
WB_ARB_2M BusArbiter (
.CLK_I(CLK_O),
.RST_I(RST_O),
.M0_ADR_I(CPU_ADR_O),
.M0_DAT_I(CPU_DAT_O),
.M0_DAT_O(CPU_DAT_I),
.M0_LOCK_I(1'b0),
.M0_CYC_I(CPU_CYC_O),
.M0_STB_I(CPU_STB_O),
.M0_WE_I(CPU_WE_O),
.M0_SEL_I(CPU_SEL_O),
.M0_ACK_O(CPU_ACK_I),
.M1_ADR_I(DMA_ADR_O),
.M1_DAT_I(DMA_DAT_O),
.M1_DAT_O(DMA_DAT_I),
.M1_LOCK_I(1'b0),
.M1_CYC_I(DMA_CYC_O),
.M1_STB_I(DMA_STB_O),
.M1_WE_I(DMA_WE_O),
.M1_SEL_I(DMA_SEL_O),
.M1_ACK_O(DMA_ACK_I),
.S_ADR_O(DATA_ADR_O),
.S_DAT_O(DATA_DAT_O),
.S_DAT_I(DATA_DAT_I),
.S_STB_O(DATA_STB_O),
.S_WE_O(DATA_WE_O),
.S_SEL_O(DATA_SEL_O),
.S_ACK_I(DATA_ACK_I),
.S_ERR_I(1'b0),
.S_RTY_I(1'b0)
);
wire [31:0] TIMER_DAT_O;
TIMER #(
.INIT_RELOAD(32'd49999)
) Timer (
.CLK_I(CLK_O),
.RST_I(RST_O),
.ADR_I(DATA_ADR_O[3:2]),
.DAT_I(DATA_DAT_O),
.DAT_O(TIMER_DAT_O),
.STB_I(DATA_STB_O & (SLAVE == `SLV_TIMER)),
.WE_I(DATA_WE_O),
.ACK_O(TIMER_ACK_O),
.IRQ_O(TIMER_IRQ_O)
);
wire RS232_TX_FULL;
wire RS232_RX_EMPTY;
wire [31:0] IRQ_DAT_O;
IRQ_CTL InterruptController (
.CLK_I(CLK_O),
.RST_I(RST_O),
.IRQ_O(IRQ_I),
.IADR_O(IADR_I),
.IACK_I(IACK_O),
.ADR_I(DATA_ADR_O[5:2]),
.DAT_I(DATA_DAT_O),
.DAT_O(IRQ_DAT_O),
.STB_I(DATA_STB_O & (SLAVE == `SLV_IRQ_CTL)),
.WE_I(DATA_WE_O),
.SEL_I(DATA_SEL_O),
.ACK_O(IRQ_ACK_O),
.IRQ0(1'b0),
.IRQ1(~RS232_RX_EMPTY),
.IRQ2(DMA_IRQ_O),
.IRQ3(~RS232_TX_FULL),
.IRQ4(TIMER_IRQ_O),
.IRQ5(1'b0),
.IRQ6(1'b0),
.IRQ7(1'b0)
);
wire [31:0] PROGRAM_DAT_O;
ProgramRAM Program (
.CLK_I(CLK_O),
.RST_I(RST_O),
.INST_ADR_I(INST_ADR_O[12:0]),
.INST_DAT_O(INST_DAT_I),
.INST_STB_I(INST_STB_O),
.INST_ACK_O(INST_ACK_I),
.DATA_ADR_I(DATA_ADR_O[12:2]),
.DATA_DAT_I(DATA_DAT_O),
.DATA_DAT_O(PROGRAM_DAT_O),
.DATA_STB_I(DATA_STB_O & (SLAVE == `SLV_PROGRAM_RAM)),
.DATA_WE_I(DATA_WE_O),
.DATA_SEL_I(DATA_SEL_O),
.DATA_ACK_O(PROGRAM_ACK_O)
);
wire [31:0] SRAM_DAT_O;
WB_SRAM WishboneSRAM (
.CLK_I(CLK_O),
.RST_I(RST_O),
.ADR_I(DATA_ADR_O[19:2]),
.DAT_I(DATA_DAT_O),
.DAT_O(SRAM_DAT_O),
.STB_I(DATA_STB_O & (SLAVE == `SLV_SRAM)),
.WE_I(DATA_WE_O),
.SEL_I(DATA_SEL_O),
.ACK_O(SRAM_ACK_O),
.SRAM_ADR(SRAM_ADR),
.SRAM_WE(SRAM_WE),
.SRAM_OE(SRAM_OE),
.SRAM_IO1(SRAM_IO1),
.SRAM_CE1(SRAM_CE1),
.SRAM_UB1(SRAM_UB1),
.SRAM_LB1(SRAM_LB1),
.SRAM_IO2(SRAM_IO2),
.SRAM_CE2(SRAM_CE2),
.SRAM_UB2(SRAM_UB2),
.SRAM_LB2(SRAM_LB2)
);
wire [31:0] RS232_DAT_O;
RS232 #(
.CLOCK_DIV(434) // 115200 bps @ 50 MHz CLKIN
) SerialInterface (
.CLK_I(CLK_O),
.RST_I(RST_O),
.DAT_I(DATA_DAT_O),
.DAT_O(RS232_DAT_O),
.STB_I(DATA_STB_O & (SLAVE == `SLV_RS232)),
.WE_I(DATA_WE_O),
.SEL_I(DATA_SEL_O),
.ACK_O(RS232_ACK_O),
.TX_FULL(RS232_TX_FULL),
.RX_EMPTY(RS232_RX_EMPTY),
.RX(RX_A),
.TX(TX_A)
);
reg [7:0] SWITCH_R;
reg [3:0] BTN_R;
reg [7:0] LED = 8'h00;
reg [19:0] DISPLAY = 20'h00000;
SevenSeg Display (
.CLKIN(CLK_O),
.INPUT(DISPLAY[15:0]),
//.INPUT(INST_ADR_O[15:0]),
//.INPUT(DATA_ADR_O[17:2]),
.DP(DISPLAY[19:16]),
.SEG(SEG),
.ANODE(ANODE)
);
always @*
begin
case (SLAVE)
`SLV_PROGRAM_RAM: DATA_DAT_I <= PROGRAM_DAT_O;
`SLV_SRAM: DATA_DAT_I <= SRAM_DAT_O;
`SLV_IRQ_CTL: DATA_DAT_I <= IRQ_DAT_O;
`SLV_DMA_CTL: DATA_DAT_I <= DMA_CTL_DAT_O;
`SLV_TIMER: DATA_DAT_I <= TIMER_DAT_O;
`SLV_LED_SWITCH: DATA_DAT_I <= { 24'h0, SWITCH_R };
`SLV_BTN_DISPLAY: DATA_DAT_I <= { 4'h0, BTN_R, 4'h0, DISPLAY };
`SLV_RS232: DATA_DAT_I <= RS232_DAT_O;
default: DATA_DAT_I <= 32'hA5A5A5A5;
endcase
case (SLAVE)
`SLV_PROGRAM_RAM: DATA_ACK_I <= PROGRAM_ACK_O;
`SLV_SRAM: DATA_ACK_I <= SRAM_ACK_O;
`SLV_IRQ_CTL: DATA_ACK_I <= IRQ_ACK_O;
`SLV_DMA_CTL: DATA_ACK_I <= DMA_CTL_ACK_O;
`SLV_TIMER: DATA_ACK_I <= TIMER_ACK_O;
`SLV_RS232: DATA_ACK_I <= RS232_ACK_O;
default: DATA_ACK_I <= DATA_STB_O;
endcase
end
reg [15:0] RST_CNT = 16'hFFFF;
always @(posedge CLK_O)
begin
if (BTN_R[3] & ~&RST_CNT)
RST_CNT <= RST_CNT + 16'd1;
else if (|RST_CNT)
RST_CNT <= RST_CNT - 16'd1;
if (RST_CNT == 16'hFFFF)
RST_O <= 1'b1;
else if (RST_CNT == 16'h0000)
RST_O <= 1'b0;
end
always @(posedge CLK_O)
begin
BTN_R <= BTN;
SWITCH_R <= SWITCH;
if (DATA_STB_O & DATA_WE_O)
begin
case (SLAVE)
`SLV_LED_SWITCH:
LED <= DATA_DAT_O[7:0];
`SLV_BTN_DISPLAY:
begin
if (DATA_SEL_O[2]) DISPLAY[19:16] <= DATA_DAT_O[19:16];
if (DATA_SEL_O[1]) DISPLAY[15: 8] <= DATA_DAT_O[15: 8];
if (DATA_SEL_O[0]) DISPLAY[ 7: 0] <= DATA_DAT_O[ 7: 0];
end
endcase
end
end
endmodule

4
toplevel.xst Normal file
View File

@ -0,0 +1,4 @@
run -ifn toplevel.prj -ifmt mixed -top TopLevel
-ofn toplevel.ngc -ofmt NGC -p xc3s200-ft256-4
-opt_mode speed -opt_level 1 -slice_utilization_ratio 100
-max_fanout 300 -rtlview yes