From 0da3692eb896c156a69ea8465cd47b1a7ffb3fc8 Mon Sep 17 00:00:00 2001 From: Jesse McDonald Date: Sat, 9 Feb 2013 12:46:27 -0600 Subject: [PATCH] Import stack-oriented CPU design from Mercurial repository. --- .gitignore | 35 ++ Makefile | 28 ++ arbiter.v | 79 +++++ asm/echo.txt | 8 + asm/include/interrupts.txt | 18 + asm/include/memory-map.txt | 39 +++ asm/lib/base64.txt | 147 ++++++++ asm/lib/memcmp.txt | 48 +++ asm/lib/rs232.txt | 54 +++ asm/lib/signed_mult.txt | 32 ++ asm/logic-test.txt | 106 ++++++ asm/monitor.txt | 268 +++++++++++++++ assemble.rb | 687 +++++++++++++++++++++++++++++++++++++ cpu.v | 346 +++++++++++++++++++ dma.v | 161 +++++++++ doc/opcode-overview.txt | 33 ++ doc/opcode-table.txt | 53 +++ fifo.v | 72 ++++ irq_ctl.v | 173 ++++++++++ nlz.v | 50 +++ program.v | 87 +++++ rs232.v | 130 +++++++ seven-seg.v | 64 ++++ sram.v | 125 +++++++ stack.v | 34 ++ timer.v | 61 ++++ toplevel.prj | 13 + toplevel.ucf | 209 +++++++++++ toplevel.v | 416 ++++++++++++++++++++++ toplevel.xst | 4 + 30 files changed, 3580 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 arbiter.v create mode 100644 asm/echo.txt create mode 100644 asm/include/interrupts.txt create mode 100644 asm/include/memory-map.txt create mode 100644 asm/lib/base64.txt create mode 100644 asm/lib/memcmp.txt create mode 100644 asm/lib/rs232.txt create mode 100644 asm/lib/signed_mult.txt create mode 100644 asm/logic-test.txt create mode 100644 asm/monitor.txt create mode 100755 assemble.rb create mode 100644 cpu.v create mode 100644 dma.v create mode 100644 doc/opcode-overview.txt create mode 100644 doc/opcode-table.txt create mode 100644 fifo.v create mode 100644 irq_ctl.v create mode 100644 nlz.v create mode 100644 program.v create mode 100644 rs232.v create mode 100644 seven-seg.v create mode 100644 sram.v create mode 100644 stack.v create mode 100644 timer.v create mode 100644 toplevel.prj create mode 100644 toplevel.ucf create mode 100644 toplevel.v create mode 100644 toplevel.xst diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eab4dd0 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8e4198a --- /dev/null +++ b/Makefile @@ -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 diff --git a/arbiter.v b/arbiter.v new file mode 100644 index 0000000..c6f80d3 --- /dev/null +++ b/arbiter.v @@ -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 diff --git a/asm/echo.txt b/asm/echo.txt new file mode 100644 index 0000000..835abbd --- /dev/null +++ b/asm/echo.txt @@ -0,0 +1,8 @@ +include "include/memory-map.txt" + +_start: + call read_char + call write_char + jump _start + +include "lib/rs232.txt" diff --git a/asm/include/interrupts.txt b/asm/include/interrupts.txt new file mode 100644 index 0000000..49a977f --- /dev/null +++ b/asm/include/interrupts.txt @@ -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 diff --git a/asm/include/memory-map.txt b/asm/include/memory-map.txt new file mode 100644 index 0000000..5d56d33 --- /dev/null +++ b/asm/include/memory-map.txt @@ -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 diff --git a/asm/lib/base64.txt b/asm/lib/base64.txt new file mode 100644 index 0000000..401f4ed --- /dev/null +++ b/asm/lib/base64.txt @@ -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 diff --git a/asm/lib/memcmp.txt b/asm/lib/memcmp.txt new file mode 100644 index 0000000..aafee3e --- /dev/null +++ b/asm/lib/memcmp.txt @@ -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 // ) diff --git a/asm/lib/rs232.txt b/asm/lib/rs232.txt new file mode 100644 index 0000000..78deb54 --- /dev/null +++ b/asm/lib/rs232.txt @@ -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 diff --git a/asm/lib/signed_mult.txt b/asm/lib/signed_mult.txt new file mode 100644 index 0000000..bc07f8f --- /dev/null +++ b/asm/lib/signed_mult.txt @@ -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 diff --git a/asm/logic-test.txt b/asm/logic-test.txt new file mode 100644 index 0000000..fe0c523 --- /dev/null +++ b/asm/logic-test.txt @@ -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 diff --git a/asm/monitor.txt b/asm/monitor.txt new file mode 100644 index 0000000..36430d1 --- /dev/null +++ b/asm/monitor.txt @@ -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: diff --git a/assemble.rb b/assemble.rb new file mode 100755 index 0000000..5d9559e --- /dev/null +++ b/assemble.rb @@ -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 diff --git a/cpu.v b/cpu.v new file mode 100644 index 0000000..5b93124 --- /dev/null +++ b/cpu.v @@ -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 diff --git a/dma.v b/dma.v new file mode 100644 index 0000000..8d9aabf --- /dev/null +++ b/dma.v @@ -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 diff --git a/doc/opcode-overview.txt b/doc/opcode-overview.txt new file mode 100644 index 0000000..3201529 --- /dev/null +++ b/doc/opcode-overview.txt @@ -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 diff --git a/doc/opcode-table.txt b/doc/opcode-table.txt new file mode 100644 index 0000000..4f2a261 --- /dev/null +++ b/doc/opcode-table.txt @@ -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 | --- | + diff --git a/fifo.v b/fifo.v new file mode 100644 index 0000000..439949f --- /dev/null +++ b/fifo.v @@ -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<> 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 diff --git a/seven-seg.v b/seven-seg.v new file mode 100644 index 0000000..7437fdf --- /dev/null +++ b/seven-seg.v @@ -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 diff --git a/sram.v b/sram.v new file mode 100644 index 0000000..3be14e5 --- /dev/null +++ b/sram.v @@ -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 diff --git a/stack.v b/stack.v new file mode 100644 index 0000000..d5a53a9 --- /dev/null +++ b/stack.v @@ -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<"); +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; diff --git a/toplevel.v b/toplevel.v new file mode 100644 index 0000000..f125b65 --- /dev/null +++ b/toplevel.v @@ -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 diff --git a/toplevel.xst b/toplevel.xst new file mode 100644 index 0000000..5685dff --- /dev/null +++ b/toplevel.xst @@ -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