├── .gitignore ├── test ├── alu.asm ├── jump.asm ├── branch.asm ├── load_byte.asm ├── store_byte.asm ├── ciscv.asm ├── blink.asm ├── uart.asm ├── registers.inc ├── spi.asm ├── bootloader.asm ├── ciscv_speed.asm ├── lcd.asm └── lcd_mandel_hw.asm ├── tools ├── bin2txt.py ├── test_multiply.py ├── lst2verilog.py └── permutation.py ├── src ├── alu.vinc ├── rom.v ├── alu.v ├── ram.v ├── mandelbrot.v ├── spi.v ├── memory_bus.v ├── uart.v ├── eeprom.v ├── peripherals.v ├── riscv.v ├── riscv_mandel.v └── ciscv.v ├── LICENSE ├── icefun.pcf ├── tangnano20k.cst ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | riscv.bin 2 | riscv.asc 3 | riscv.json 4 | abc.history 5 | rom.bin 6 | rom.txt 7 | rom.lst 8 | *.swp 9 | -------------------------------------------------------------------------------- /test/alu.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .org 0x4000 4 | 5 | main: 6 | li t1, 0xc000 7 | slli t1, t1, 16 8 | srai t1, t1, 18 9 | ori x6, x0, 5 10 | ebreak 11 | 12 | -------------------------------------------------------------------------------- /test/jump.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .org 0x4000 4 | 5 | main: 6 | li t1, 0xc000 7 | j blah 8 | ebreak 9 | 10 | blah: 11 | li t1, 0xc008 12 | ebreak 13 | 14 | -------------------------------------------------------------------------------- /test/branch.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .org 0x4000 4 | 5 | main: 6 | li t1, 0xc000 7 | bne zero, zero, blah 8 | ebreak 9 | 10 | blah: 11 | li t1, 0xc008 12 | ebreak 13 | 14 | -------------------------------------------------------------------------------- /test/load_byte.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .org 0x4000 4 | main: 5 | lui x6, 0x12345 6 | ori x6, x6, 0x678 7 | 8 | li x5, 0x4008 9 | 10 | lb t1, 5(x5) 11 | ;lh t1, -2(x5) 12 | ebreak 13 | 14 | -------------------------------------------------------------------------------- /test/store_byte.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .org 0x4000 4 | main: 5 | li t0, 0xc089 6 | li t5, 100 7 | 8 | sb t5, 1(t0) 9 | lb t1, 1(t0) 10 | ;sh t5, 0(t0) 11 | ;lh t1, 2(t0) 12 | 13 | ebreak 14 | 15 | -------------------------------------------------------------------------------- /test/ciscv.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .org 0x4000 4 | 5 | main: 6 | ;li.w t1, 0x12345678 7 | ;li t3, 0x4000 8 | ;lw.pre t1, 4(t3) 9 | ;lw.post t1, 4(t3) 10 | 11 | li t1, 0x4000 12 | ;lw.pre t3, 4(t1) 13 | lw.post t3, 4(t1) 14 | ebreak 15 | 16 | -------------------------------------------------------------------------------- /tools/bin2txt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | fp = open(sys.argv[1], "rb") 6 | 7 | while True: 8 | b = fp.read(4) 9 | if not b: break 10 | #print("%02x%02x%02x%02x" % (b[0], b[1], b[2], b[3])) 11 | print("%02x%02x%02x%02x" % (b[3], b[2], b[1], b[0])) 12 | 13 | fp.close() 14 | 15 | -------------------------------------------------------------------------------- /test/blink.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .org 0x4000 4 | main: 5 | li s0, 0x8000 6 | 7 | main_loop: 8 | li a0, 1 9 | sb a0, 0x20(s0) 10 | jal delay 11 | 12 | li a0, 0 13 | sb a0, 0x20(s0) 14 | jal delay 15 | 16 | j main_loop 17 | 18 | delay: 19 | li t0, 0x30000 20 | delay_loop: 21 | addi t0, t0, -1 22 | bne t0, zero, delay_loop 23 | ret 24 | 25 | -------------------------------------------------------------------------------- /src/alu.vinc: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2025 by Michael Kohn 9 | 10 | parameter ALU_OP_ADD = 0; 11 | parameter ALU_OP_SLL = 1; 12 | parameter ALU_OP_SLT = 2; 13 | parameter ALU_OP_SLTU = 3; 14 | parameter ALU_OP_XOR = 4; 15 | parameter ALU_OP_SRL = 5; 16 | parameter ALU_OP_OR = 6; 17 | parameter ALU_OP_AND = 7; 18 | 19 | -------------------------------------------------------------------------------- /test/uart.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .include "test/registers.inc" 4 | 5 | .org 0x4000 6 | 7 | main: 8 | li gp, 0x8000 9 | 10 | li t0, 1 11 | sw t0, PORT0(gp) 12 | 13 | li t0, 'x' 14 | sw t0, UART_TX(gp) 15 | jal delay 16 | 17 | li t0, 'y' 18 | sw t0, UART_TX(gp) 19 | jal delay 20 | 21 | li t0, 'Z' 22 | sw t0, UART_TX(gp) 23 | jal delay 24 | 25 | li t0, 0 26 | sw t0, PORT0(gp) 27 | 28 | while_1: 29 | j while_1 30 | 31 | delay: 32 | li t0, 4000 33 | delay_loop: 34 | addi t0, t0, -1 35 | bnez t0, delay_loop 36 | ret 37 | 38 | -------------------------------------------------------------------------------- /src/rom.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2025 by Michael Kohn 9 | 10 | // This is a hardcoded program that blinks an external LED. 11 | 12 | module rom 13 | ( 14 | input [11:0] address, 15 | output reg [31:0] data_out, 16 | input clk 17 | ); 18 | 19 | reg [31:0] memory [1023:0]; 20 | 21 | initial begin 22 | $readmemh("rom.txt", memory); 23 | end 24 | 25 | always @(posedge clk) begin 26 | data_out <= memory[address[11:2]]; 27 | end 28 | 29 | endmodule 30 | 31 | -------------------------------------------------------------------------------- /test/registers.inc: -------------------------------------------------------------------------------- 1 | 2 | ;; Registers. 3 | BUTTON equ 0x00 4 | SPI_TX equ 0x04 5 | SPI_RX equ 0x08 6 | SPI_CTL equ 0x0c 7 | PORT0 equ 0x20 8 | SOUND equ 0x24 9 | SPI_IO equ 0x28 10 | UART_TX equ 0x38 11 | UART_RX equ 0x3c 12 | UART_CTL equ 0x40 13 | 14 | ;; Bits in SPI_CTL. 15 | SPI_BUSY equ 0x01 16 | SPI_START equ 0x02 17 | SPI_16 equ 0x04 18 | 19 | ;; Bits in UART_CTL. 20 | UART_TX_BUSY equ 0x01 21 | UART_RX_READY equ 0x02 22 | 23 | ;; Bits in SPI_IO. 24 | LCD_RES equ 0x01 25 | LCD_DC equ 0x02 26 | LCD_CS equ 0x04 27 | 28 | ;; Bits in PORT0 29 | LED0 equ 0x01 30 | 31 | -------------------------------------------------------------------------------- /tools/test_multiply.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | def show_value(v, f): 4 | n = 1 5 | 6 | if (v & 0x8000) != 0: 7 | v = ((v ^ 0xffff) + 1) & 0xffff 8 | n = -1 9 | 10 | d = v * n; 11 | print("decimal=%d fixed=%04x" % (d, f)) 12 | 13 | def multiply(a, b): 14 | a = a & 0xffff 15 | b = b & 0xffff 16 | 17 | s = "%04x * %04x" % (a, b) 18 | 19 | print(" -- " + str(a) + " * " + str(b) + " -- " + s) 20 | show_value(a, a) 21 | show_value(b, b) 22 | 23 | v = 0 24 | 25 | for i in range(0, 16): 26 | if (a & 1) == 1: 27 | v = v + b 28 | 29 | a = a >> 1 30 | b = b << 1 31 | 32 | f = (v >> 10) & 0xffff 33 | v = v & 0xffff 34 | 35 | show_value(v, f) 36 | 37 | # -------------------------- fold here ------------------------ 38 | 39 | multiply(-100, 150) 40 | multiply(150, -100) 41 | multiply(0xf800, 0xf800) 42 | multiply(0xfc00, 0xfc00) 43 | 44 | multiply(0xf800 + 0x600, 0xf800 + 0x600) 45 | multiply(0xfc00 + 0x400, 0xfc00 + 0x400) 46 | multiply(0xff00, 0xff00) 47 | 48 | print("4 << 10 = %04x" % (4 << 10)) 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Michael Kohn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/alu.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2025 by Michael Kohn 9 | 10 | `include "alu.vinc" 11 | 12 | module alu 13 | ( 14 | input [31:0] source, 15 | input signed [31:0] arg_1, 16 | input [2:0] alu_op, 17 | input is_alt, 18 | output reg [31:0] result 19 | ); 20 | 21 | always @ * begin 22 | case (alu_op) 23 | ALU_OP_ADD: 24 | if (is_alt == 0) 25 | result <= $signed(source) + arg_1; 26 | else 27 | result <= $signed(source) - arg_1; 28 | ALU_OP_SLL: result <= source << arg_1; 29 | ALU_OP_SLT: result <= $signed(source) < arg_1; 30 | ALU_OP_SLTU: result <= source < $unsigned(arg_1); 31 | ALU_OP_XOR: result <= source ^ arg_1; 32 | ALU_OP_SRL: 33 | if (is_alt == 0) 34 | result <= source >> arg_1; 35 | else 36 | result <= $signed(source) >>> arg_1; 37 | ALU_OP_OR: result <= source | arg_1; 38 | ALU_OP_AND: result <= source & arg_1; 39 | endcase 40 | end 41 | 42 | endmodule 43 | 44 | -------------------------------------------------------------------------------- /test/spi.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .org 0x4000 4 | 5 | ;; Registers. 6 | BUTTON equ 0x00 7 | SPI_TX equ 0x04 8 | SPI_RX equ 0x08 9 | SPI_CTL equ 0x0c 10 | PORT0 equ 0x20 11 | SOUND equ 0x24 12 | SPI_IO equ 0x28 13 | 14 | ;; Bits in SPI_CTL. 15 | SPI_BUSY equ 0x01 16 | SPI_START equ 0x02 17 | SPI_16 equ 0x04 18 | 19 | ;; Bits in SPI_IO. 20 | LCD_RES equ 0x01 21 | LCD_DC equ 0x02 22 | LCD_CS equ 0x04 23 | 24 | ;; Bits in PORT0 25 | LED0 equ 0x01 26 | 27 | main: 28 | li gp, 0x8000 29 | li a0, 0xaaaa 30 | li t0, 0 31 | sw t0, PORT0(gp) 32 | 33 | ;; lcd_send_data(a0) 34 | lcd_send_data: 35 | li t0, LCD_DC | LCD_RES 36 | sw t0, SPI_IO(gp) 37 | sw a0, SPI_TX(gp) 38 | li t0, SPI_16 | SPI_START 39 | sw t0, SPI_CTL(gp) 40 | lcd_send_data_wait: 41 | ;lw t0, SPI_CTL(gp) 42 | ;andi t0, t0, SPI_BUSY 43 | ;bnez t0, lcd_send_data_wait 44 | ;nop 45 | li t0, LCD_CS | LCD_RES 46 | sw t0, SPI_IO(gp) 47 | 48 | li t0, 1 49 | sw t0, PORT0(gp) 50 | 51 | while_1: 52 | j while_1 53 | nop 54 | 55 | delay: 56 | li t0, 4000 57 | delay_loop: 58 | addi t0, t0, -1 59 | bnez t0, delay_loop 60 | nop 61 | ret 62 | nop 63 | 64 | -------------------------------------------------------------------------------- /icefun.pcf: -------------------------------------------------------------------------------- 1 | # iceFUN board. 2 | 3 | # Debug LEDs. 4 | set_io --warn-no-port leds[0] C10 5 | set_io --warn-no-port leds[1] A10 6 | set_io --warn-no-port leds[2] D7 7 | set_io --warn-no-port leds[3] D6 8 | set_io --warn-no-port leds[4] A7 9 | set_io --warn-no-port leds[5] C7 10 | set_io --warn-no-port leds[6] A4 11 | set_io --warn-no-port leds[7] C4 12 | set_io --warn-no-port column[0] A12 13 | set_io --warn-no-port column[1] D10 14 | set_io --warn-no-port column[2] A6 15 | set_io --warn-no-port column[3] C5 16 | 17 | # GPIO. 18 | set_io --warn-no-port ioport_0 C14 19 | set_io --warn-no-port ioport_1 G14 20 | set_io --warn-no-port ioport_2 H12 21 | set_io --warn-no-port ioport_3 J12 22 | 23 | # SPI eeprom. 24 | #set_io --warn-no-port eeprom_cs E12 25 | #set_io --warn-no-port eeprom_clk E14 26 | #set_io --warn-no-port eeprom_di F12 27 | #set_io --warn-no-port eeprom_do F14 28 | 29 | # SPI peripheral. 30 | #set_io --warn-no-port spi_cs J12 31 | set_io --warn-no-port spi_clk K12 32 | set_io --warn-no-port spi_mosi K14 33 | set_io --warn-no-port spi_miso H11 34 | 35 | # UART 36 | set_io --warn-no-port uart_tx_0 H3 37 | set_io --warn-no-port uart_rx_0 G3 38 | 39 | # Clock signal. 40 | set_io --warn-no-port raw_clk P7 41 | 42 | # Speaker. 43 | set_io --warn-no-port speaker_p M12 44 | set_io --warn-no-port speaker_m M6 45 | 46 | # Buttons. 47 | set_io --warn-no-port button_reset A5 48 | set_io --warn-no-port button_halt A11 49 | set_io --warn-no-port button_program_select C6 50 | set_io --warn-no-port button_0 C11 51 | 52 | -------------------------------------------------------------------------------- /tools/lst2verilog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | print("// RISC-V FPGA Soft Processor") 6 | print("// Author: Michael Kohn") 7 | print("// Email: mike@mikekohn.net") 8 | print("// Web: https://www.mikekohn.net/") 9 | print("// Board: iceFUN iCE40 HX8K") 10 | print("// License: MIT") 11 | print("//") 12 | print("// Copyright 2023 by Michael Kohn\n") 13 | 14 | print("// This is a hardcoded program that blinks an external LED.\n") 15 | 16 | print("module rom") 17 | print("(") 18 | print(" input [9:0] address,") 19 | print(" output [31:0] data_out,") 20 | print(" input clk") 21 | print(");\n") 22 | 23 | print("reg [31:0] data;") 24 | print("assign data_out = data;\n") 25 | 26 | print("always @(clk) begin") 27 | print(" case (address[9:2])") 28 | 29 | indent = " " 30 | address = 0 31 | 32 | fp = open(sys.argv[1], "r") 33 | 34 | for line in fp: 35 | if not "cycles" in line: continue 36 | line = line.strip() 37 | opcodes = line[:22].strip().split()[1] 38 | code = line[22:line.find("cycles")].strip() 39 | print(indent + "// " + code) 40 | 41 | opcodes = [ 42 | opcodes[8:10], 43 | opcodes[6:8], 44 | opcodes[4:6], 45 | opcodes[2:4], 46 | ] 47 | 48 | #for opcode in opcodes: 49 | # print(indent + str(address) + ": data <= 8'h" + opcode + ";") 50 | # address += 1 51 | 52 | print(indent + str(address) + ": data <= 32'h" + opcodes[3] + opcodes[2] + opcodes[1] + opcodes[0] + ";") 53 | address += 1 54 | 55 | fp.close() 56 | 57 | print(" default: data <= 0;") 58 | print(" endcase") 59 | print("end\n") 60 | 61 | print("endmodule\n") 62 | 63 | -------------------------------------------------------------------------------- /tangnano20k.cst: -------------------------------------------------------------------------------- 1 | 2 | IO_LOC "raw_clk" 4; 3 | CLOCK_LOC "raw_clk" BUFG; 4 | 5 | //IO_LOC "leds[0]" 15; 6 | //IO_LOC "leds[1]" 16; 7 | //IO_LOC "leds[2]" 17; 8 | //IO_LOC "leds[3]" 18; 9 | //IO_LOC "leds[4]" 19; 10 | //IO_LOC "leds[5]" 20; 11 | 12 | // GPIO. 13 | IO_LOC "ioport_0" 73; 14 | IO_LOC "ioport_1" 74; 15 | IO_LOC "ioport_2" 75; 16 | IO_LOC "ioport_3" 85; 17 | //IO_LOC "ioport_4" 77; 18 | 19 | //SPI peripheral 0. 20 | //IO_LOC "spi_cs" 76; 21 | IO_LOC "spi_clk" 80; 22 | IO_LOC "spi_mosi" 42; 23 | IO_LOC "spi_miso" 41; 24 | 25 | // SPI Peripheral 1 (SparkFun SerLCD). 26 | //IO_LOC "spi_cs_1" 55; 27 | //IO_LOC "spi_mosi_1" 49; 28 | //IO_LOC "spi_miso_1" 86; 29 | //IO_LOC "spi_clk_1" 79; 30 | 31 | // UART 32 | IO_LOC "uart_tx_0" 72; 33 | IO_LOC "uart_rx_0" 71; 34 | 35 | // Speaker. 36 | IO_LOC "speaker_p" 56; 37 | IO_LOC "speaker_m" 54; 38 | 39 | // Buttons. 40 | IO_LOC "button_reset" 87; 41 | IO_LOC "button_halt" 88; 42 | IO_LOC "button_program_select" 53; 43 | IO_LOC "button_0" 52; 44 | 45 | IO_PORT "button_reset" IO_TYPE=LVCMOS33; 46 | IO_PORT "button_halt" IO_TYPE=LVCMOS33; 47 | IO_PORT "button_program_select" IO_TYPE=LVCMOS33; 48 | IO_PORT "button_0" IO_TYPE=LVCMOS33; 49 | 50 | // Windbond W25Q128JV 16MB flash memory. 51 | // do connects to do pin on flash chip and is an input to the w65c832. 52 | // di connects to di pin on flash chip and is an output from the w65c832. 53 | //IO_LOC "windbond_reset" 28; 54 | //IO_LOC "windbond_wp" 26; 55 | //IO_LOC "windbond_do" 25; 56 | //IO_LOC "windbond_di" 29; 57 | //IO_LOC "windbond_clk" 30; 58 | //IO_LOC "windbond_cs" 31; 59 | 60 | -------------------------------------------------------------------------------- /src/ram.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2025 by Michael Kohn 9 | 10 | // This creates 1024 bytes of RAM on the FPGA itself. Written this 11 | // way makes it inferred by the IceStorm tools. It seems like it 12 | // only infers it to BlockRam when using double_clk, which based 13 | // on the timing chart in the Lattice documentation seems to make 14 | // sense. 15 | 16 | module ram 17 | ( 18 | input [11:0] address, 19 | input [31:0] data_in, 20 | output reg [31:0] data_out, 21 | //output reg [7:0] debug, 22 | input [3:0] write_mask, 23 | input write_enable, 24 | input clk 25 | ); 26 | 27 | reg [7:0] storage_0 [1023:0]; 28 | reg [7:0] storage_1 [1023:0]; 29 | reg [7:0] storage_2 [1023:0]; 30 | reg [7:0] storage_3 [1023:0]; 31 | 32 | wire [9:0] aligned_address; 33 | assign aligned_address = address[11:2]; 34 | 35 | //always @(posedge double_clk) begin 36 | always @(posedge clk) begin 37 | if (write_enable) begin 38 | //debug <= write_mask; 39 | 40 | if (!write_mask[0]) storage_0[aligned_address] <= data_in[7:0]; 41 | if (!write_mask[1]) storage_1[aligned_address] <= data_in[15:8]; 42 | if (!write_mask[2]) storage_2[aligned_address] <= data_in[23:16]; 43 | if (!write_mask[3]) storage_3[aligned_address] <= data_in[31:24]; 44 | end else begin 45 | data_out[7:0] <= storage_0[aligned_address]; 46 | data_out[15:8] <= storage_1[aligned_address]; 47 | data_out[23:16] <= storage_2[aligned_address]; 48 | data_out[31:24] <= storage_3[aligned_address]; 49 | end 50 | end 51 | 52 | endmodule 53 | 54 | -------------------------------------------------------------------------------- /tools/permutation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | def permutate(data): 4 | out = { } 5 | 6 | for i in range(0, 32): 7 | out[i] = -1 8 | 9 | bit = 31 10 | 11 | for d in data: 12 | if len(d) > 1 and d[1] == -1: 13 | bit -= d[0] 14 | continue 15 | 16 | if len(d) == 1: 17 | out[d[0]] = bit 18 | bit -= 1 19 | continue 20 | 21 | i = d[0] 22 | while i >= d[1]: 23 | out[i] = bit 24 | i -= 1 25 | bit -= 1 26 | 27 | print(len(out)) 28 | 29 | c = -2 30 | count = 0 31 | ranges = [ ] 32 | seq = -1 33 | 34 | for i in range(31, -1, -1): 35 | value = out[i] 36 | print(str(value) + " ", end="") 37 | 38 | if value == -1 and c == -1: 39 | count += 1 40 | continue 41 | 42 | if c == -1: 43 | ranges.append(str(count) + "'b" + ("0" * count)) 44 | count = 0 45 | c = -2 46 | 47 | while True: 48 | if (c == -2): 49 | c = value 50 | seq = value 51 | break 52 | else: 53 | if value != seq - 1: 54 | if c == seq: 55 | ranges.append("[" + str(c) + "]") 56 | else: 57 | ranges.append("[" + str(c) + ":" + str(seq) + "]") 58 | c = -2 59 | continue 60 | 61 | seq -= 1 62 | break 63 | 64 | if c != -2: 65 | if c == -1: 66 | ranges.append(str(count) + "'b" + ("0" * count)) 67 | else: 68 | ranges.append("[" + str(c) + ":" + str(seq) + "]") 69 | 70 | print() 71 | 72 | print(ranges) 73 | 74 | print("jal") 75 | permutate([ [20], [10,1], [11], [19,12], [12,-1] ]) 76 | 77 | print("jalr") 78 | permutate([ [11,0], [20,-1] ]) 79 | 80 | print("branch") 81 | permutate([ [12], [10,5], [13,-1], [4,1], [11], [7,-1] ]) 82 | 83 | -------------------------------------------------------------------------------- /src/mandelbrot.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023 by Michael Kohn 9 | 10 | module mandelbrot 11 | ( 12 | input raw_clk, 13 | input start, 14 | input [15:0] curr_r, 15 | input [15:0] curr_i, 16 | output [3:0] result, 17 | output busy 18 | ); 19 | 20 | reg is_running = 0; 21 | 22 | reg [1:0] state = 0; 23 | reg signed [15:0] zr; 24 | reg signed [15:0] zi; 25 | reg signed [31:0] zr2; 26 | reg signed [31:0] zi2; 27 | reg signed [15:0] tr; 28 | reg signed [31:0] ti; 29 | reg [3:0] count; 30 | 31 | assign result = count; 32 | assign busy = is_running; 33 | 34 | parameter STATE_IDLE = 0; 35 | parameter STATE_START = 1; 36 | parameter STATE_CHECK_SQUARE = 2; 37 | parameter STATE_LAST = 3; 38 | 39 | always @(posedge raw_clk) begin 40 | case (state) 41 | STATE_IDLE: 42 | begin 43 | if (start) begin 44 | is_running <= 1; 45 | zr <= curr_r; 46 | zi <= curr_i; 47 | state <= STATE_START; 48 | count <= 15; 49 | end else begin 50 | is_running <= 0; 51 | end 52 | end 53 | STATE_START: 54 | begin 55 | zr2 <= (zr * zr) >> 10; 56 | zi2 <= (zi * zi) >> 10; 57 | state <= STATE_CHECK_SQUARE; 58 | end 59 | STATE_CHECK_SQUARE: 60 | begin 61 | if ((zr2[15:0] + zi2[15:0]) >= (4 << 10)) begin 62 | state <= STATE_IDLE; 63 | end else begin 64 | tr <= zr2[15:0] - zi2[15:0]; 65 | ti <= (zr * zi) >> 9; 66 | state = STATE_LAST; 67 | end 68 | end 69 | STATE_LAST: 70 | begin 71 | if (count == 0) begin 72 | state <= STATE_IDLE; 73 | end else begin 74 | zr <= tr + curr_r; 75 | zi <= ti[15:0] + curr_i; 76 | state <= STATE_START; 77 | count <= count - 1; 78 | end 79 | end 80 | endcase 81 | end 82 | 83 | endmodule 84 | 85 | -------------------------------------------------------------------------------- /src/spi.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2025 by Michael Kohn 9 | 10 | module spi 11 | ( 12 | input raw_clk, 13 | input start, 14 | input width_16, 15 | input [15:0] data_tx, 16 | output [7:0] data_rx, 17 | output busy, 18 | output reg sclk, 19 | output reg mosi, 20 | input miso 21 | ); 22 | 23 | // FIXME: Width only supports 8 bit on the receive side. 24 | reg [7:0] rx_buffer; 25 | reg [15:0] tx_buffer; 26 | reg [4:0] count; 27 | 28 | parameter STATE_IDLE = 0; 29 | parameter STATE_CLOCK_0 = 1; 30 | parameter STATE_CLOCK_1 = 2; 31 | parameter STATE_LAST = 3; 32 | 33 | reg [1:0] state = STATE_IDLE; 34 | assign data_rx = rx_buffer; 35 | assign busy = state != STATE_IDLE; 36 | 37 | always @(posedge raw_clk) begin 38 | case (state) 39 | STATE_IDLE: 40 | begin 41 | if (start) begin 42 | if (width_16) 43 | tx_buffer <= data_tx; 44 | else 45 | tx_buffer[15:8] <= data_tx[7:0]; 46 | 47 | state <= STATE_CLOCK_0; 48 | count <= 0; 49 | end else begin 50 | mosi <= 0; 51 | end 52 | end 53 | STATE_CLOCK_0: 54 | begin 55 | sclk <= 0; 56 | 57 | if (count != 0) rx_buffer <= { rx_buffer[6:0], miso }; 58 | 59 | tx_buffer <= tx_buffer << 1; 60 | //tx_buffer <= { tx_buffer[14:0], 1'b0 }; 61 | mosi <= tx_buffer[15]; 62 | 63 | count <= count + 1; 64 | state <= STATE_CLOCK_1; 65 | end 66 | STATE_CLOCK_1: 67 | begin 68 | sclk <= 1; 69 | 70 | if (width_16 == 0 && count[3]) begin 71 | state <= STATE_LAST; 72 | end else if (width_16 == 1 && count[4]) begin 73 | state <= STATE_LAST; 74 | end else begin 75 | state <= STATE_CLOCK_0; 76 | end 77 | end 78 | STATE_LAST: 79 | begin 80 | sclk <= 0; 81 | rx_buffer <= { rx_buffer[6:0], miso }; 82 | state <= STATE_IDLE; 83 | end 84 | endcase 85 | end 86 | 87 | endmodule 88 | 89 | -------------------------------------------------------------------------------- /test/bootloader.asm: -------------------------------------------------------------------------------- 1 | ; XMODEM Bootloader for Michael Kohn's RISC-V FPGA Core 2 | ; Written by Joe Davisson 3 | 4 | ; Tested with: 5 | ; - iceFUN iCE40 HX8K FPGA 6 | ; - Adafruit USB to TTL serial cable 7 | ; - Minicom 2.9 (9600 baud, 8N2, no handshaking) 8 | 9 | ; cable connnections: 10 | ; - RED - 5V (not connected) 11 | ; - BLACK - GND 12 | ; - WHITE - RX (goes to H3) 13 | ; - GREEN - TX (goes to G3) 14 | 15 | ; packet format: 16 | ; - start of header (1 byte) 17 | ; - packet number (1 byte) 18 | ; - inverse packet_number (1 byte) 19 | ; - data (128 bytes) 20 | ; - checksum (1 byte) 21 | 22 | .riscv 23 | 24 | .include "test/registers.inc" 25 | 26 | SOH equ 0x01 ; start of header 27 | EOT equ 0x04 ; end of transmission 28 | ACK equ 0x06 ; acknowledged 29 | NAK equ 0x15 ; not acknowledged 30 | 31 | address equ s10 32 | packet_count equ s11 33 | 34 | ; program is loaded into RAM here 35 | ram_start equ 0x0000 36 | 37 | ; program loader 38 | .org 0x4000 39 | 40 | start: 41 | ; UART base 42 | li gp, 0x8000 43 | 44 | message: 45 | li s0, message_text 46 | message_loop: 47 | lbu a0, (s0) 48 | beqz a0, button_press 49 | jal write_uart 50 | addi s0, s0, 1 51 | j message_loop 52 | 53 | button_press: 54 | lbu s0, BUTTON(gp) 55 | andi s0, s0, 1 56 | beqz s0, button_press 57 | 58 | button_release: 59 | lbu s0, BUTTON(gp) 60 | andi s0, s0, 1 61 | bnez s0, button_release 62 | 63 | begin_transfer: 64 | li address, ram_start 65 | li packet_count, 1 66 | 67 | li a0, NAK 68 | jal write_uart 69 | 70 | get_next_packet: 71 | jal read_uart 72 | li s0, SOH 73 | beq a0, s0, check_packet_count 74 | li s0, EOT 75 | bne a0, s0, get_next_packet_error 76 | j done 77 | get_next_packet_error: 78 | j error 79 | 80 | check_packet_count: 81 | jal read_uart 82 | beq a0, packet_count, check_packet_count_inverse 83 | j error 84 | 85 | check_packet_count_inverse: 86 | mv s0, a0 87 | jal read_uart 88 | add s0, s0, a0 89 | li s1, 0xff 90 | beq s0, s1, get_packet_data 91 | j error 92 | 93 | get_packet_data: 94 | mv s0, address 95 | mv s1, address 96 | addi s1, s1, 128 97 | get_packet_data_loop: 98 | jal read_uart 99 | sb a0, (s0) 100 | addi s0, s0, 1 101 | bne s0, s1, get_packet_data_loop 102 | 103 | test_checksum: 104 | jal read_uart 105 | mv s0, address 106 | mv s1, address 107 | addi s1, s1, 128 108 | li s2, 0 109 | test_checksum_loop: 110 | lbu s4, (s0) 111 | add s2, s2, s4 112 | addi s0, s0, 1 113 | bne s0, s1, test_checksum_loop 114 | andi s2, s2, 0xff 115 | beq s2, a0, update_address 116 | j error 117 | 118 | update_address: 119 | addi address, address, 128 120 | addi packet_count, packet_count, 1 121 | andi packet_count, packet_count, 0xff 122 | 123 | li a0, ACK 124 | jal write_uart 125 | j get_next_packet 126 | 127 | done: 128 | li a0, ACK 129 | jal write_uart 130 | 131 | ; run program 132 | j ram_start 133 | 134 | error: 135 | li a0, NAK 136 | jal write_uart 137 | j get_next_packet 138 | 139 | read_uart: 140 | lbu t0, UART_CTL(gp) 141 | andi t0, t0, UART_RX_READY 142 | beqz t0, read_uart 143 | lbu a0, UART_RX(gp) 144 | ret 145 | 146 | write_uart: 147 | sb a0, UART_TX(gp) 148 | write_uart_wait: 149 | lbu t0, UART_CTL(gp) 150 | andi t0, t0, UART_TX_BUSY 151 | bnez t0, write_uart_wait 152 | ret 153 | 154 | message_text: 155 | .ascii "Ready.\r\n\0\0\0\0" 156 | 157 | -------------------------------------------------------------------------------- /src/memory_bus.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2025 by Michael Kohn 9 | 10 | // The purpose of this module is to route reads and writes to the 4 11 | // different memory banks. Originally the idea was to have ROM and RAM 12 | // be SPI EEPROM (this may be changed in the future) so there would also 13 | // need a "ready" signal that would pause the CPU until the data can be 14 | // clocked in and out of of the SPI chips. 15 | 16 | module memory_bus 17 | ( 18 | input [15:0] address, 19 | input [31:0] data_in, 20 | input [3:0] write_mask, 21 | output [31:0] data_out, 22 | //output [7:0] debug, 23 | input bus_enable, 24 | input write_enable, 25 | //input clk, 26 | input raw_clk, 27 | output speaker_p, 28 | output speaker_m, 29 | output ioport_0, 30 | output ioport_1, 31 | output ioport_2, 32 | output ioport_3, 33 | input button_0, 34 | input reset, 35 | output spi_clk, 36 | output spi_mosi, 37 | input spi_miso, 38 | output uart_tx_0, 39 | input uart_rx_0 40 | ); 41 | 42 | wire [31:0] rom_data_out; 43 | wire [31:0] ram_data_out; 44 | wire [31:0] peripherals_data_out; 45 | wire [31:0] block_ram_data_out; 46 | 47 | wire ram_write_enable; 48 | wire peripherals_write_enable; 49 | wire block_ram_write_enable; 50 | 51 | assign ram_write_enable = (address[15:14] == 2'b00) && write_enable; 52 | assign peripherals_write_enable = (address[15:14] == 2'b10) && write_enable; 53 | assign block_ram_write_enable = (address[15:14] == 2'b11) && write_enable; 54 | 55 | // FIXME: The RAM probably need an enable also. 56 | wire peripherals_enable; 57 | assign peripherals_enable = (address[15:14] == 2'b10) && bus_enable; 58 | 59 | // Based on the selected bank of memory (address[15:14]) select if 60 | // memory should read from ram.v, rom.v, peripherals.v. 61 | assign data_out = address[15] == 0 ? 62 | (address[14] == 0 ? ram_data_out : rom_data_out) : 63 | (address[14] == 0 ? peripherals_data_out : block_ram_data_out); 64 | 65 | rom rom_0( 66 | .address (address[11:0]), 67 | .data_out (rom_data_out), 68 | .clk (raw_clk) 69 | ); 70 | 71 | ram ram_0( 72 | .address (address[11:0]), 73 | .data_in (data_in), 74 | .data_out (ram_data_out), 75 | //.debug (debug), 76 | .write_mask (write_mask), 77 | .write_enable (ram_write_enable), 78 | .clk (raw_clk) 79 | ); 80 | 81 | peripherals peripherals_0( 82 | .enable (peripherals_enable), 83 | .address (address[7:0]), 84 | .data_in (data_in), 85 | .data_out (peripherals_data_out), 86 | //.debug (debug), 87 | .write_enable (peripherals_write_enable), 88 | //.clk (clk), 89 | .raw_clk (raw_clk), 90 | .speaker_p (speaker_p), 91 | .speaker_m (speaker_m), 92 | .ioport_0 (ioport_0), 93 | .ioport_1 (ioport_1), 94 | .ioport_2 (ioport_2), 95 | .ioport_3 (ioport_3), 96 | .button_0 (button_0), 97 | .reset (reset), 98 | .spi_clk (spi_clk), 99 | .spi_mosi (spi_mosi), 100 | .spi_miso (spi_miso), 101 | .uart_tx_0 (uart_tx_0), 102 | .uart_rx_0 (uart_rx_0) 103 | ); 104 | 105 | ram ram_1( 106 | .address (address[11:0]), 107 | .data_in (data_in), 108 | .data_out (block_ram_data_out), 109 | //.debug (debug), 110 | .write_mask (write_mask), 111 | .write_enable (block_ram_write_enable), 112 | .clk (raw_clk) 113 | ); 114 | 115 | endmodule 116 | 117 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | NAKEN_INCLUDE=../naken_asm/include 3 | PROGRAM=riscv 4 | SOURCE= \ 5 | src/alu.v \ 6 | src/memory_bus.v \ 7 | src/peripherals.v \ 8 | src/ram.v \ 9 | src/rom.v \ 10 | src/spi.v \ 11 | src/uart.v 12 | 13 | EXTRA_SOURCE= \ 14 | src/eeprom.v \ 15 | src/mandelbrot.v \ 16 | 17 | default: 18 | yosys -q \ 19 | -p "synth_ice40 -top $(PROGRAM) -json $(PROGRAM).json" \ 20 | $(SOURCE) src/riscv.v 21 | nextpnr-ice40 -r \ 22 | --hx8k \ 23 | --json $(PROGRAM).json \ 24 | --package cb132 \ 25 | --asc $(PROGRAM).asc \ 26 | --opt-timing \ 27 | --pcf icefun.pcf 28 | icepack $(PROGRAM).asc $(PROGRAM).bin 29 | 30 | tang_nano: 31 | yosys -q \ 32 | -D TANG_NANO \ 33 | -p "read_verilog src/riscv.v $(SOURCE); synth_gowin -json $(PROGRAM).json -family gw2a" 34 | nextpnr-himbaechel -r \ 35 | --json $(PROGRAM).json \ 36 | --write $(PROGRAM)_pnr.json \ 37 | --freq 27 \ 38 | --vopt family=GW2A-18C \ 39 | --vopt cst=tangnano20k.cst \ 40 | --device GW2AR-LV18QN88C8/I7 41 | gowin_pack -d GW2A-18C -o $(PROGRAM).fs $(PROGRAM)_pnr.json 42 | 43 | riscv_mandel: 44 | yosys -q -p "synth_ice40 -top $(PROGRAM) -json $(PROGRAM).json" $(SOURCE) $(EXTRA_SOURCE) src/riscv_mandel.v 45 | nextpnr-ice40 -r --hx8k --json $(PROGRAM).json --package cb132 --asc $(PROGRAM).asc --opt-timing --pcf icefun.pcf 46 | icepack $(PROGRAM).asc $(PROGRAM).bin 47 | 48 | timing: 49 | icetime -t -d hx8k -p icefun.pcf $(PROGRAM).asc 50 | 51 | ciscv: 52 | yosys -q -p "synth_ice40 -top $(PROGRAM) -json $(PROGRAM).json" $(SOURCE) $(EXTRA_SOURCE) src/ciscv.v 53 | nextpnr-ice40 -r --hx8k --json $(PROGRAM).json --package cb132 --asc $(PROGRAM).asc --opt-timing --pcf icefun.pcf 54 | icepack $(PROGRAM).asc $(PROGRAM).bin 55 | 56 | program: 57 | iceFUNprog $(PROGRAM).bin 58 | 59 | blink: 60 | naken_asm -l -type bin -o rom.bin test/blink.asm 61 | python3 tools/bin2txt.py rom.bin > rom.txt 62 | 63 | lcd: 64 | naken_asm -l -type bin -o rom.bin -I$(NAKEN_INCLUDE) test/lcd.asm 65 | python3 tools/bin2txt.py rom.bin > rom.txt 66 | 67 | lcd_mandel_hw: 68 | naken_asm -l -type bin -o rom.bin test/lcd_mandel_hw.asm 69 | python3 tools/bin2txt.py rom.bin > rom.txt 70 | 71 | ciscv_test: 72 | naken_asm -l -type bin -o rom.bin test/ciscv.asm 73 | python3 tools/bin2txt.py rom.bin > rom.txt 74 | 75 | ciscv_speed: 76 | naken_asm -l -type bin -o rom.bin test/ciscv_speed.asm 77 | python3 tools/bin2txt.py rom.bin > rom.txt 78 | 79 | store_byte: 80 | naken_asm -l -type bin -o rom.bin test/store_byte.asm 81 | python3 tools/bin2txt.py rom.bin > rom.txt 82 | 83 | load_byte: 84 | naken_asm -l -type bin -o rom.bin test/load_byte.asm 85 | python3 tools/bin2txt.py rom.bin > rom.txt 86 | 87 | alu: 88 | naken_asm -l -type bin -o rom.bin test/alu.asm 89 | python3 tools/bin2txt.py rom.bin > rom.txt 90 | 91 | jump: 92 | naken_asm -l -type bin -o rom.bin test/jump.asm 93 | python3 tools/bin2txt.py rom.bin > rom.txt 94 | 95 | branch: 96 | naken_asm -l -type bin -o rom.bin test/branch.asm 97 | python3 tools/bin2txt.py rom.bin > rom.txt 98 | 99 | spi: 100 | naken_asm -l -type bin -o rom.bin test/spi.asm 101 | python3 tools/bin2txt.py rom.bin > rom.txt 102 | 103 | uart: 104 | naken_asm -l -type bin -o rom.bin test/uart.asm 105 | python3 tools/bin2txt.py rom.bin > rom.txt 106 | 107 | bootloader: 108 | naken_asm -l -type bin -o rom.bin test/bootloader.asm 109 | python3 tools/bin2txt.py rom.bin > rom.txt 110 | 111 | clean: 112 | @rm -f $(PROGRAM).bin $(PROGRAM).json $(PROGRAM).asc *.lst 113 | @rm -f blink.bin load_byte.bin store_byte.bin test_subroutine.bin 114 | @rm -f button.bin 115 | @echo "Clean!" 116 | 117 | -------------------------------------------------------------------------------- /src/uart.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2024 by Michael Kohn 9 | 10 | module uart 11 | ( 12 | input raw_clk, 13 | input [7:0] tx_data, 14 | input tx_strobe, 15 | //output reg tx_busy, 16 | output tx_busy, 17 | output reg tx_pin, 18 | output reg [7:0] rx_data, 19 | output rx_ready, 20 | input rx_ready_clear, 21 | input rx_pin 22 | ); 23 | 24 | reg [3:0] tx_count; 25 | reg [3:0] rx_count; 26 | 27 | reg [9:0] rx_buffer; 28 | reg [9:0] tx_buffer; 29 | 30 | parameter STATE_IDLE = 0; 31 | parameter STATE_TX_NEXT_BIT = 1; 32 | 33 | parameter STATE_RX_BIT_FRONT = 1; 34 | parameter STATE_RX_BIT_BACK = 2; 35 | 36 | reg [1:0] rx_state = STATE_IDLE; 37 | reg [1:0] tx_state = STATE_IDLE; 38 | 39 | assign tx_busy = tx_state != STATE_IDLE; 40 | 41 | reg rx_ready_flag = 0; 42 | assign rx_ready = rx_ready_flag; 43 | 44 | // 12,000,000 MHz / 9600 = 1250 clocks. 45 | // 12,000,000 MHz / 9600 = 625 clocks for double speed to make sure 46 | // bits are clocked in / out in the center of each bit transmission. 47 | reg [10:0] tx_divisor; 48 | reg [9:0] rx_divisor; 49 | 50 | // Transmit. 51 | always @(posedge raw_clk) begin 52 | case (tx_state) 53 | STATE_IDLE: 54 | begin 55 | // Wait for the CPU to strobe to start a read. 56 | if (tx_strobe) begin 57 | tx_buffer[0] <= 0; 58 | tx_buffer[8:1] <= tx_data; 59 | tx_buffer[9] <= 1; 60 | tx_count <= 0; 61 | tx_divisor <= 0; 62 | tx_state <= STATE_TX_NEXT_BIT; 63 | end else begin 64 | tx_pin <= 1; 65 | end 66 | end 67 | STATE_TX_NEXT_BIT: 68 | begin 69 | if (tx_divisor == 1249) begin 70 | tx_divisor <= 0; 71 | if (tx_count == 10) tx_state <= STATE_IDLE; 72 | end else begin 73 | tx_divisor <= tx_divisor + 1; 74 | end 75 | 76 | if (tx_divisor == 0) begin 77 | tx_pin <= tx_buffer[tx_count]; 78 | tx_count <= tx_count + 1; 79 | end 80 | end 81 | endcase 82 | end 83 | 84 | // Receive. 85 | always @(posedge raw_clk) begin 86 | if (rx_ready_clear) rx_ready_flag <= 0; 87 | 88 | case (rx_state) 89 | STATE_IDLE: 90 | begin 91 | // Wait for start bit to start a read. 92 | if (rx_pin == 0) begin 93 | rx_divisor <= 0; 94 | rx_count <= 0; 95 | rx_state <= STATE_RX_BIT_FRONT; 96 | end 97 | end 98 | STATE_RX_BIT_FRONT: 99 | begin 100 | if (rx_divisor == 624) begin 101 | rx_divisor <= 0; 102 | rx_state <= STATE_RX_BIT_BACK; 103 | end else begin 104 | rx_divisor <= rx_divisor + 1; 105 | end 106 | end 107 | STATE_RX_BIT_BACK: 108 | begin 109 | if (rx_divisor == 624) begin 110 | rx_divisor <= 0; 111 | 112 | if (rx_count == 10) begin 113 | // Make sure stop bit is high or the data is invalid. 114 | if (rx_buffer[9] == 1) begin 115 | rx_data <= rx_buffer[8:1]; 116 | rx_ready_flag <= 1; 117 | end 118 | 119 | rx_state <= STATE_IDLE; 120 | end else begin 121 | rx_state <= STATE_RX_BIT_FRONT; 122 | end 123 | end else begin 124 | rx_divisor <= rx_divisor + 1; 125 | end 126 | 127 | if (rx_divisor == 0) begin 128 | rx_buffer[rx_count] <= rx_pin; 129 | rx_count <= rx_count + 1; 130 | end 131 | end 132 | endcase 133 | end 134 | 135 | endmodule 136 | 137 | -------------------------------------------------------------------------------- /src/eeprom.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023 by Michael Kohn 9 | 10 | // This module reads from an AT93C86A EEPROM chip. 11 | // Data format: 110 AAAAAAAAAA DDDDDDDD where: 12 | // Binary 110 is 3 bits telling the EEPROM to go into "read" mode. 13 | // AAAAAAAAAA is 10 bits of the memory address being requested. 14 | // DDDDDDDD is 8 bits clocked out of the EEPROM being the data at that address. 15 | 16 | module eeprom 17 | ( 18 | input [10:0] address, 19 | input strobe, 20 | input raw_clk, 21 | output reg eeprom_cs, 22 | output reg eeprom_clk, 23 | output reg eeprom_di, 24 | input eeprom_do, 25 | output ready, 26 | output reg [7:0] data_out 27 | ); 28 | 29 | // Data sheet says 2.7v to 5.5v has a max speed of 1MHz. raw_clk should be 30 | // 12MHz, so divide by 16. 31 | //reg [4:0] clock_div; 32 | reg [2:0] delay = 0; 33 | //wire clk; 34 | //assign clk = clock_div[4]; 35 | 36 | reg [13:0] command; 37 | reg [3:0] count; 38 | //reg running = 0; 39 | 40 | parameter STATE_IDLE = 0; 41 | parameter STATE_SEND_ADDRESS_0 = 1; 42 | parameter STATE_SEND_ADDRESS_1 = 2; 43 | parameter STATE_READ_START = 3; 44 | parameter STATE_READ_DATA_0 = 4; 45 | parameter STATE_READ_DATA_1 = 5; 46 | parameter STATE_FINISH = 6; 47 | 48 | reg [2:0] state = STATE_FINISH; 49 | 50 | assign ready = state == STATE_IDLE; 51 | 52 | // To run the AT93C86A at a speed slower than 2MHz, divide the clock down. 53 | /* 54 | always @(posedge raw_clk) begin 55 | clock_div <= clock_div + 1; 56 | end 57 | */ 58 | 59 | // State machine for reading the SPI-like EEPROM. 60 | always @(posedge raw_clk) begin 61 | case (state) 62 | STATE_IDLE: 63 | begin 64 | // Wait for the CPU to strobe to start a read. 65 | if (strobe) begin 66 | command[13:11] <= 3'b110; 67 | command[10:0] <= address; 68 | count <= 14; 69 | eeprom_cs <= 1; 70 | state <= STATE_SEND_ADDRESS_0; 71 | end else begin 72 | eeprom_cs <= 0; 73 | eeprom_di <= 0; 74 | eeprom_clk <= 0; 75 | end 76 | end 77 | STATE_SEND_ADDRESS_0: 78 | begin 79 | // Clock out 3 bits of command and 11 bits of 80 | // address to the EEPROM. 81 | case (delay) 82 | 0: 83 | begin 84 | count <= count - 1; 85 | eeprom_di <= command[13]; 86 | command[13:1] <= command[12:0]; 87 | eeprom_clk <= 0; 88 | end 89 | 7: 90 | state <= STATE_SEND_ADDRESS_1; 91 | endcase 92 | 93 | delay <= delay + 1; 94 | end 95 | STATE_SEND_ADDRESS_1: 96 | begin 97 | eeprom_clk <= 1; 98 | 99 | if (delay == 7) begin 100 | if (count == 0) begin 101 | state <= STATE_READ_START; 102 | end else begin 103 | state <= STATE_SEND_ADDRESS_0; 104 | end 105 | end 106 | 107 | delay <= delay + 1; 108 | end 109 | STATE_READ_START: 110 | begin 111 | eeprom_clk <= 0; 112 | eeprom_di <= 0; 113 | count <= 8; 114 | state <= STATE_READ_DATA_0; 115 | end 116 | STATE_READ_DATA_0: 117 | begin 118 | // Clock in 8 bits of data from the EEPROM. 119 | case (delay) 120 | 1: 121 | begin 122 | count <= count - 1; 123 | data_out[7:1] <= data_out[6:0]; 124 | eeprom_clk <= 1; 125 | end 126 | 7: 127 | state <= STATE_READ_DATA_1; 128 | endcase 129 | 130 | delay <= delay + 1; 131 | end 132 | STATE_READ_DATA_1: 133 | begin 134 | eeprom_clk <= 0; 135 | 136 | case (delay) 137 | 0: 138 | data_out[0] <= eeprom_do; 139 | 7: 140 | begin 141 | if (count == 0) begin 142 | state <= STATE_FINISH; 143 | end else begin 144 | state <= STATE_READ_DATA_0; 145 | end 146 | end 147 | endcase 148 | 149 | delay <= delay + 1; 150 | end 151 | STATE_FINISH: 152 | begin 153 | // Go back to IDLE state where the ready signal will tell the 154 | // the CPU that data is available. 155 | eeprom_cs <= 0; 156 | eeprom_di <= 0; 157 | state <= STATE_IDLE; 158 | end 159 | endcase 160 | end 161 | 162 | endmodule 163 | 164 | -------------------------------------------------------------------------------- /test/ciscv_speed.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .org 0xc000 4 | 5 | ;; Registers. 6 | BUTTON equ 0x00 7 | SPI_TX equ 0x04 8 | SPI_RX equ 0x08 9 | SPI_CTL equ 0x0c 10 | PORT0 equ 0x20 11 | SOUND equ 0x24 12 | SPI_IO equ 0x28 13 | 14 | ;; Bits in SPI_CTL. 15 | SPI_BUSY equ 0x01 16 | SPI_START equ 0x02 17 | SPI_16 equ 0x04 18 | 19 | ;; Bits in SPI_IO. 20 | LCD_RES equ 0x01 21 | LCD_DC equ 0x02 22 | LCD_CS equ 0x04 23 | 24 | ;; Bits in PORT0 25 | LED0 equ 0x01 26 | 27 | COMMAND_DISPLAY_OFF equ 0xae 28 | COMMAND_SET_REMAP equ 0xa0 29 | COMMAND_START_LINE equ 0xa1 30 | COMMAND_DISPLAY_OFFSET equ 0xa2 31 | COMMAND_NORMAL_DISPLAY equ 0xa4 32 | COMMAND_SET_MULTIPLEX equ 0xa8 33 | COMMAND_SET_MASTER equ 0xad 34 | COMMAND_POWER_MODE equ 0xb0 35 | COMMAND_PRECHARGE equ 0xb1 36 | COMMAND_CLOCKDIV equ 0xb3 37 | COMMAND_PRECHARGE_A equ 0x8a 38 | COMMAND_PRECHARGE_B equ 0x8b 39 | COMMAND_PRECHARGE_C equ 0x8c 40 | COMMAND_PRECHARGE_LEVEL equ 0xbb 41 | COMMAND_VCOMH equ 0xbe 42 | COMMAND_MASTER_CURRENT equ 0x87 43 | COMMAND_CONTRASTA equ 0x81 44 | COMMAND_CONTRASTB equ 0x82 45 | COMMAND_CONTRASTC equ 0x83 46 | COMMAND_DISPLAY_ON equ 0xaf 47 | 48 | .define mandel mulw 49 | 50 | .macro send_command(value) 51 | li a0, value 52 | jal lcd_send_cmd 53 | .endm 54 | 55 | .macro square_fixed(result, var) 56 | .scope 57 | mv a1, var 58 | li t1, 0x8000 59 | and t1, t1, a1 60 | beqz t1, not_signed 61 | li t1, 0xffff 62 | xor a1, a1, t1 63 | addi a1, a1, 1 64 | and a1, a1, t1 65 | not_signed: 66 | mv a2, a1 67 | jal multiply 68 | mv result, a3 69 | .ends 70 | .endm 71 | 72 | .macro multiply_signed(var_0, var_1) 73 | .scope 74 | mv a1, var_0 75 | mv a2, var_1 76 | li t2, 0x0000 77 | li t1, 0x8000 78 | and t1, t1, a1 79 | beqz t1, not_signed_0 80 | xor t2, t2, t1 81 | xor a1, a1, t3 82 | addi a1, a1, 1 83 | and a1, a1, t3 84 | not_signed_0: 85 | li t1, 0x8000 86 | and t1, t1, a2 87 | beqz t1, not_signed_1 88 | xor t2, t2, t1 89 | xor a2, a2, t3 90 | addi a2, a2, 1 91 | and a2, a2, t3 92 | not_signed_1: 93 | jal multiply 94 | beqz t2, dont_add_sign 95 | xor a3, a3, t3 96 | addi a3, a3, 1 97 | and a3, a3, t3 98 | dont_add_sign: 99 | .ends 100 | .endm 101 | 102 | start: 103 | ;; Point gp to peripherals. 104 | li gp, 0x8000 105 | ;; Clear LED. 106 | li t0, 1 107 | sw t0, PORT0(gp) 108 | li s11, 0 109 | 110 | main: 111 | jal lcd_init 112 | jal lcd_clear 113 | ;jal lcd_clear_2 114 | 115 | li s2, 0 116 | main_while_1: 117 | lw t0, BUTTON(gp) 118 | andi t0, t0, 1 119 | bnez t0, run 120 | xori s2, s2, 1 121 | sb s2, PORT0(gp) 122 | jal delay 123 | 124 | j main_while_1 125 | 126 | run: 127 | jal lcd_clear_3 128 | 129 | ;; Copy memory using RISC-V only. 130 | li t0, 300 131 | run_riscv_outer_loop: 132 | li t1, 0x4000 133 | li t2, 0xc000 134 | li t3, 4096 135 | run_riscv_copy_loop: 136 | lb t4, 0(t1) 137 | sb t4, 0(t2) 138 | addi t1, t1, 1 139 | addi t2, t2, 1 140 | addi t3, t3, -1 141 | bne zero, t3, run_riscv_copy_loop 142 | addi t0, t0, -1 143 | bne zero, t0, run_riscv_outer_loop 144 | 145 | jal lcd_clear_2 146 | 147 | ;; Copy memory using CISC-V extra instructions. 148 | li t0, 300 149 | run_ciscv_outer_loop: 150 | li t1, 0x4000 151 | li t2, 0xc000 152 | li t3, 4096 153 | run_ciscv_copy_loop: 154 | lb.post t4, 1(t1) 155 | sb.post t4, 1(t2) 156 | addi t3, t3, -1 157 | bne zero, t3, run_ciscv_copy_loop 158 | addi t0, t0, -1 159 | bne zero, t0, run_ciscv_outer_loop 160 | 161 | jal lcd_clear 162 | j main_while_1 163 | 164 | lcd_init: 165 | mv s1, ra 166 | li t0, LCD_CS 167 | sw t0, SPI_IO(gp) 168 | jal delay 169 | li t0, LCD_CS | LCD_RES 170 | sw t0, SPI_IO(gp) 171 | 172 | send_command(COMMAND_DISPLAY_OFF) 173 | send_command(COMMAND_SET_REMAP) 174 | send_command(0x72) 175 | send_command(COMMAND_START_LINE) 176 | send_command(0x00) 177 | send_command(COMMAND_DISPLAY_OFFSET) 178 | send_command(0x00) 179 | send_command(COMMAND_NORMAL_DISPLAY) 180 | send_command(COMMAND_SET_MULTIPLEX) 181 | send_command(0x3f) 182 | send_command(COMMAND_SET_MASTER) 183 | send_command(0x8e) 184 | send_command(COMMAND_POWER_MODE) 185 | send_command(COMMAND_PRECHARGE) 186 | send_command(0x31) 187 | send_command(COMMAND_CLOCKDIV) 188 | send_command(0xf0) 189 | send_command(COMMAND_PRECHARGE_A) 190 | send_command(0x64) 191 | send_command(COMMAND_PRECHARGE_B) 192 | send_command(0x78) 193 | send_command(COMMAND_PRECHARGE_C) 194 | send_command(0x64) 195 | send_command(COMMAND_PRECHARGE_LEVEL) 196 | send_command(0x3a) 197 | send_command(COMMAND_VCOMH) 198 | send_command(0x3e) 199 | send_command(COMMAND_MASTER_CURRENT) 200 | send_command(0x06) 201 | send_command(COMMAND_CONTRASTA) 202 | send_command(0x91) 203 | send_command(COMMAND_CONTRASTB) 204 | send_command(0x50) 205 | send_command(COMMAND_CONTRASTC) 206 | send_command(0x7d) 207 | send_command(COMMAND_DISPLAY_ON) 208 | jalr zero, s1, 0 209 | 210 | lcd_clear: 211 | mv s1, ra 212 | li t1, 96 * 64 213 | li a0, 0xff0f 214 | lcd_clear_loop: 215 | jal lcd_send_data 216 | addi t1, t1, -1 217 | bnez t1, lcd_clear_loop 218 | jalr zero, s1, 0 219 | 220 | lcd_clear_2: 221 | mv s1, ra 222 | li t1, 96 * 64 223 | li a0, 0xf00f 224 | lcd_clear_loop_2: 225 | jal lcd_send_data 226 | addi t1, t1, -1 227 | bnez t1, lcd_clear_loop_2 228 | jalr zero, s1, 0 229 | 230 | lcd_clear_3: 231 | mv s1, ra 232 | li t1, 96 * 64 233 | li a0, 0xf0ff 234 | lcd_clear_loop_3: 235 | jal lcd_send_data 236 | addi t1, t1, -1 237 | bnez t1, lcd_clear_loop_3 238 | jalr zero, s1, 0 239 | 240 | ;; lcd_send_cmd(a0) 241 | lcd_send_cmd: 242 | li t0, LCD_RES 243 | sw t0, SPI_IO(gp) 244 | sw a0, SPI_TX(gp) 245 | li t0, SPI_START 246 | sw t0, SPI_CTL(gp) 247 | lcd_send_cmd_wait: 248 | lw t0, SPI_CTL(gp) 249 | andi t0, t0, SPI_BUSY 250 | bnez t0, lcd_send_cmd_wait 251 | li t0, LCD_CS | LCD_RES 252 | sw t0, SPI_IO(gp) 253 | ret 254 | 255 | ;; lcd_send_data(a0) 256 | lcd_send_data: 257 | li t0, LCD_DC | LCD_RES 258 | sw t0, SPI_IO(gp) 259 | sw a0, SPI_TX(gp) 260 | li t0, SPI_16 | SPI_START 261 | sw t0, SPI_CTL(gp) 262 | lcd_send_data_wait: 263 | lw t0, SPI_CTL(gp) 264 | andi t0, t0, SPI_BUSY 265 | bnez t0, lcd_send_data_wait 266 | li t0, LCD_CS | LCD_RES 267 | sw t0, SPI_IO(gp) 268 | ret 269 | 270 | delay: 271 | li t0, 65536 272 | delay_loop: 273 | addi t0, t0, -1 274 | bnez t0, delay_loop 275 | ret 276 | 277 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RISC-V 2 | ====== 3 | 4 | This is an implementation of a RISC-V CPU implemented in Verilog 5 | to be run on an FPGA. The board being used here is an iceFUN with 6 | a Lattice iCE40 HX8K FPGA. 7 | 8 | This project was created by forking the Intel 8008 FPGA project 9 | changing the memory_bus.v module to take a 32 bit databus and 10 | changing the core processor to decode RISC-V instructions. 11 | 12 | https://www.mikekohn.net/micro/riscv_fpga.php 13 | https://www.mikekohn.net/micro/intel_8008_fpga.php 14 | 15 | Features 16 | ======== 17 | 18 | IO, Button input, speaker tone generator, SPI, and Mandelbrot acceleration. 19 | 20 | Instructions 21 | ============ 22 | 23 | lb rd, offset(rs1) iiii iiii iiii sssss 000 ddddd 0000 011 24 | lh rd, offset(rs1) iiii iiii iiii sssss 001 ddddd 0000 011 25 | lw rd, offset(rs1) iiii iiii iiii sssss 010 ddddd 0000 011 26 | lbu rd, offset(rs1) iiii iiii iiii sssss 100 ddddd 0000 011 27 | lhu rd, offset(rs1) iiii iiii iiii sssss 101 ddddd 0000 011 28 | 29 | addi rd, rs1, imm iiii iiii iiii sssss 000 ddddd 0010 011 30 | slti rd, rs1, imm iiii iiii iiii sssss 010 ddddd 0010 011 31 | sltiu rd, rs1, imm iiii iiii iiii sssss 011 ddddd 0010 011 32 | xori rd, rs1, imm iiii iiii iiii sssss 100 ddddd 0010 011 33 | ori rd, rs1, imm iiii iiii iiii sssss 110 ddddd 0010 011 34 | andi rd, rs1, imm iiii iiii iiii sssss 111 ddddd 0010 011 35 | slli rd, rs1, imm 0000 000 shamt sssss 001 ddddd 0010 011 36 | srli rd, rs1, imm 0000 000 shamt sssss 101 ddddd 0010 011 37 | srai rd, rs1, imm 0100 000 shamt sssss 101 ddddd 0010 011 38 | 39 | sb rd, offset(rs1) iiii iii rrrrr sssss 000 iiiii 0100 011 40 | sh rd, offset(rs1) iiii iii rrrrr sssss 001 iiiii 0100 011 41 | sw rd, offset(rs1) iiii iii rrrrr sssss 010 iiiii 0100 011 42 | 43 | add rd, rs1, rs2 0000 000 rrrrr sssss 000 ddddd 0110 011 44 | sub rd, rs1, rs2 0100 000 rrrrr sssss 000 ddddd 0110 011 45 | sll rd, rs1, rs2 0000 000 rrrrr sssss 001 ddddd 0110 011 46 | slt rd, rs1, rs2 0000 000 rrrrr sssss 010 ddddd 0110 011 47 | sltu rd, rs1, rs2 0000 000 rrrrr sssss 011 ddddd 0110 011 48 | xor rd, rs1, rs2 0000 000 rrrrr sssss 100 ddddd 0110 011 49 | srl rd, rs1, rs2 0000 000 rrrrr sssss 101 ddddd 0110 011 50 | sra rd, rs1, rs2 0100 000 rrrrr sssss 101 ddddd 0110 011 51 | or rd, rs1, rs2 0000 000 rrrrr sssss 110 ddddd 0110 011 52 | and rd, rs1, rs2 0000 000 rrrrr sssss 111 ddddd 0110 011 53 | 54 | beq rs1, rs2, offset iiii iii rrrrr sssss 000 iiiii 1100 011 55 | bne rs1, rs2, offset iiii iii rrrrr sssss 001 iiiii 1100 011 56 | blt rs1, rs2, offset iiii iii rrrrr sssss 100 iiiii 1100 011 57 | bge rs1, rs2, offset iiii iii rrrrr sssss 101 iiiii 1100 011 58 | bltu rs1, rs2, offset iiii iii rrrrr sssss 110 iiiii 1100 011 59 | bgeu rs1, rs2, offset iiii iii rrrrr sssss 111 iiiii 1100 011 60 | 61 | ebreak 0000 0000 0000 00000 000 00000 1110 011 62 | 63 | auipc rd, imm iiii iiii iiii iiii iiii ddddd 0010 111 64 | lui rd, imm iiii iiii iiii iiii iiii ddddd 0110 111 65 | jalr rd, rs1, offset iiii iiii iiii sssss 000 ddddd 1100 111 66 | jal rd, offset iiii iiii iiii iiii iiii ddddd 1101 111 67 | 68 | For src/riscv_mandel.v, this instruction is encoded as mulw. 69 | 70 | mandel rd, rs1, rs2 0000 001 rrrrr sssss 000 ddddd 0111011 71 | 72 | Registers 73 | ========= 74 | x0 zero 75 | x1 ra 76 | x2 sp 77 | x3 gp 78 | x4 tp 79 | x5 - x7 t0 - t2 80 | x8 s0 / fp 81 | x9 s1 82 | x10 - x17 a0 - a7 83 | x18 - x27 s2 - s11 84 | x28 - x31 t3 - t6 85 | 86 | Memory Map 87 | ========== 88 | 89 | This implementation of the RISC-V has 4 banks of memory. Each address 90 | contains a 16 bit word instead of 8 bit byte like a typical CPU. 91 | 92 | * Bank 0: 0x0000 RAM (4096 bytes) 93 | * Bank 1: 0x4000 ROM 94 | * Bank 2: 0x8000 Peripherals 95 | * Bank 3: 0xc000 RAM (4096 bytes) 96 | 97 | On start-up by default, the chip will load a program from a AT93C86A 98 | 2kB EEPROM with a 3-Wire (SPI-like) interface but wll run the code 99 | from the ROM. To start the program loaded to RAM, the program select 100 | button needs to be held down while the chip is resetting. 101 | 102 | The peripherals area contain the following: 103 | 104 | * 0x8000: input from push button 105 | * 0x8004: SPI TX buffer 106 | * 0x8008: SPI RX buffer 107 | * 0x800c: SPI control: bit 2: 8/16, bit 1: start strobe, bit 0: busy 108 | * 0x8020: ioport_A output (in my test case only 1 pin is connected) 109 | * 0x8024: MIDI note value (60-96) to play a tone on the speaker or 0 to stop 110 | * 0x8028: ioport_B output (3 pins) 111 | * 0x8038: UART TX buffer 112 | * 0x803c: UART RX buffer 113 | * 0x8040: UART status: bit 1: rx_ready, bit 0: tx_busy 114 | 115 | IO 116 | -- 117 | 118 | iport_A is just 1 output in my test circuit to an LED. 119 | iport_B is 3 outputs used in my test circuit for SPI (RES/CS/DC) to the LCD. 120 | 121 | MIDI 122 | ---- 123 | 124 | The MIDI note peripheral allows the iceFUN board to play tones at specified 125 | frequencies based on MIDI notes. 126 | 127 | SPI 128 | --- 129 | 130 | The SPI peripheral has 3 memory locations. One location for reading 131 | data after it's received, one location for filling the transmit buffer, 132 | and one location for signaling. 133 | 134 | For signaling, setting bit 1 to a 1 will cause whatever is in the TX 135 | buffer to be transmitted. Until the data is fully transmitted, bit 0 136 | will be set to 1 to let the user know the SPI bus is busy. 137 | 138 | There is also the ability to do 16 bit transfers by setting bit 2 to 1. 139 | 140 | UART 141 | ---- 142 | 143 | The UART runs only at 9600 baud. Reading from UART RX clears rx_ready. 144 | 145 | Mandelbrot 146 | ---------- 147 | 148 | The mulw instruction opcode is implemented as a mandelbrot instruction 149 | and is tied to the mandelbrot.v module. 150 | 151 | mandel t0, t1, t2 152 | 153 | In this case t1 should contain a 16 bit fixed point value in 6.10 format 154 | representing the real value, t2 will have the fixed point imaginary value, 155 | and after something like 2 to 45 CPU cycles, t0 will contain a number between 156 | 0 to 15 with the value of that coordinate in the Mandelbrot set. 157 | 158 | An example of its use is in lcd.asm and a video of the speed difference is 159 | shown on the project website. 160 | 161 | -------------------------------------------------------------------------------- /test/lcd.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | .include "test/registers.inc" 4 | .include "lcd/ssd1331.inc" 5 | 6 | ;; Set to 0xc000 for eeprom. 7 | ;.org 0xc000 8 | .org 0x4000 9 | 10 | .define mandel mulw 11 | 12 | .macro send_command(value) 13 | li a0, value 14 | jal lcd_send_cmd 15 | .endm 16 | 17 | .macro square_fixed(result, var) 18 | .scope 19 | mv a1, var 20 | li t1, 0x8000 21 | and t1, t1, a1 22 | beqz t1, not_signed 23 | li t1, 0xffff 24 | xor a1, a1, t1 25 | addi a1, a1, 1 26 | and a1, a1, t1 27 | not_signed: 28 | mv a2, a1 29 | jal multiply 30 | mv result, a3 31 | .ends 32 | .endm 33 | 34 | .macro multiply_signed(var_0, var_1) 35 | .scope 36 | mv a1, var_0 37 | mv a2, var_1 38 | li t2, 0x0000 39 | li t1, 0x8000 40 | and t1, t1, a1 41 | beqz t1, not_signed_0 42 | xor t2, t2, t1 43 | xor a1, a1, t3 44 | addi a1, a1, 1 45 | and a1, a1, t3 46 | not_signed_0: 47 | li t1, 0x8000 48 | and t1, t1, a2 49 | beqz t1, not_signed_1 50 | xor t2, t2, t1 51 | xor a2, a2, t3 52 | addi a2, a2, 1 53 | and a2, a2, t3 54 | not_signed_1: 55 | jal multiply 56 | beqz t2, dont_add_sign 57 | xor a3, a3, t3 58 | addi a3, a3, 1 59 | and a3, a3, t3 60 | dont_add_sign: 61 | .ends 62 | .endm 63 | 64 | start: 65 | ;; Point gp to peripherals. 66 | li gp, 0x8000 67 | ;; Clear LED. 68 | li t0, 1 69 | sw t0, PORT0(gp) 70 | 71 | main: 72 | jal lcd_init 73 | jal lcd_clear 74 | 75 | li s2, 0 76 | main_while_1: 77 | lw t0, BUTTON(gp) 78 | andi t0, t0, 1 79 | bnez t0, run 80 | xori s2, s2, 1 81 | sb s2, PORT0(gp) 82 | jal delay 83 | 84 | j main_while_1 85 | 86 | run: 87 | jal ra, lcd_clear_2 88 | jal ra, mandelbrot 89 | li s2, 1 90 | j main_while_1 91 | 92 | lcd_init: 93 | mv s1, ra 94 | li t0, LCD_CS 95 | sw t0, SPI_IO(gp) 96 | jal delay 97 | li t0, LCD_CS | LCD_RES 98 | sw t0, SPI_IO(gp) 99 | 100 | send_command(SSD1331_DISPLAY_OFF) 101 | send_command(SSD1331_SET_REMAP) 102 | send_command(0x72) 103 | send_command(SSD1331_START_LINE) 104 | send_command(0x00) 105 | send_command(SSD1331_DISPLAY_OFFSET) 106 | send_command(0x00) 107 | send_command(SSD1331_DISPLAY_NORMAL) 108 | send_command(SSD1331_SET_MULTIPLEX) 109 | send_command(0x3f) 110 | send_command(SSD1331_SET_MASTER) 111 | send_command(0x8e) 112 | send_command(SSD1331_POWER_MODE) 113 | send_command(SSD1331_PRECHARGE) 114 | send_command(0x31) 115 | send_command(SSD1331_CLOCKDIV) 116 | send_command(0xf0) 117 | send_command(SSD1331_PRECHARGE_A) 118 | send_command(0x64) 119 | send_command(SSD1331_PRECHARGE_B) 120 | send_command(0x78) 121 | send_command(SSD1331_PRECHARGE_C) 122 | send_command(0x64) 123 | send_command(SSD1331_PRECHARGE_LEVEL) 124 | send_command(0x3a) 125 | send_command(SSD1331_VCOMH) 126 | send_command(0x3e) 127 | send_command(SSD1331_MASTER_CURRENT) 128 | send_command(0x06) 129 | send_command(SSD1331_CONTRAST_A) 130 | send_command(0x91) 131 | send_command(SSD1331_CONTRAST_B) 132 | send_command(0x50) 133 | send_command(SSD1331_CONTRAST_C) 134 | send_command(0x7d) 135 | send_command(SSD1331_DISPLAY_ON) 136 | jalr zero, s1, 0 137 | 138 | lcd_clear: 139 | mv s1, ra 140 | li t1, 96 * 64 141 | li a0, 0xff0f 142 | lcd_clear_loop: 143 | jal lcd_send_data 144 | addi t1, t1, -1 145 | bnez t1, lcd_clear_loop 146 | jalr zero, s1, 0 147 | 148 | lcd_clear_2: 149 | mv s1, ra 150 | li t1, 96 * 64 151 | li a0, 0xf00f 152 | lcd_clear_loop_2: 153 | jal lcd_send_data 154 | addi t1, t1, -1 155 | bnez t1, lcd_clear_loop_2 156 | jalr zero, s1, 0 157 | 158 | ;; multiply(a1, a2) -> a3 159 | multiply: 160 | ;mv a3, a1 161 | ;srli a3, a3, 1 162 | ;ret 163 | li a3, 0 164 | li t0, 16 165 | multiply_repeat: 166 | andi a4, a1, 1 167 | beqz a4, multiply_ignore_bit 168 | add a3, a3, a2 169 | multiply_ignore_bit: 170 | slli a2, a2, 1 171 | srli a1, a1, 1 172 | addi t0, t0, -1 173 | bnez t0, multiply_repeat 174 | srai a3, a3, 10 175 | li t0, 0xffff 176 | and a3, a3, t0 177 | ret 178 | 179 | mandelbrot: 180 | mv s1, ra 181 | 182 | ;; final int DEC_PLACE = 10; 183 | ;; final int r0 = (-2 << DEC_PLACE); 184 | ;; final int i0 = (-1 << DEC_PLACE); 185 | ;; final int r1 = (1 << DEC_PLACE); 186 | ;; final int i1 = (1 << DEC_PLACE); 187 | ;; final int dx = (r1 - r0) / 96; (0x0020) 188 | ;; final int dy = (i1 - i0) / 64; (0x0020) 189 | 190 | ;; Mask for 16 bit. 191 | li t3, 0xffff 192 | 193 | ;; for (y = 0; y < 64; y++) 194 | li s3, 64 195 | ;; int i = -1 << 10; 196 | li s5, 0xfc00 197 | mandelbrot_for_y: 198 | 199 | ;; for (x = 0; x < 96; x++) 200 | li s2, 96 201 | ;; int r = -2 << 10; 202 | li s4, 0xf800 203 | mandelbrot_for_x: 204 | ;; zr = r; 205 | ;; zi = i; 206 | mv s6, s4 207 | mv s7, s5 208 | 209 | ;; for (int count = 0; count < 15; count++) 210 | li a0, 15 211 | mandelbrot_for_count: 212 | ;; zr2 = (zr * zr) >> DEC_PLACE; 213 | square_fixed(a6, s6) 214 | 215 | ;; zi2 = (zi * zi) >> DEC_PLACE; 216 | square_fixed(a7, s7) 217 | 218 | ;; if (zr2 + zi2 > (4 << DEC_PLACE)) { break; } 219 | ;; cmp does: 4 - (zr2 + zi2).. if it's negative it's bigger than 4. 220 | add a5, a6, a7 221 | li t0, 4 << 10 222 | slt a5, a5, t0 223 | beqz a5, mandelbrot_stop 224 | 225 | ;; tr = zr2 - zi2; 226 | sub a5, a6, a7 227 | and a5, a5, t3 228 | 229 | ;; ti = ((zr * zi * 2) >> DEC_PLACE) << 1; 230 | multiply_signed(s6, s7) 231 | slli a3, a3, 1 232 | ;mv t1, a3 233 | 234 | ;; zr = tr + curr_r; 235 | add s6, a5, s4 236 | and s6, s6, t3 237 | 238 | ;; zi = ti + curr_i; 239 | add s7, a3, s5 240 | and s7, s7, t3 241 | 242 | addi a0, a0, -1 243 | bnez a0, mandelbrot_for_count 244 | mandelbrot_stop: 245 | 246 | slli a0, a0, 1 247 | li t0, colors 248 | add a0, t0, a0 249 | lhu a0, 0(a0) 250 | 251 | jal lcd_send_data 252 | 253 | addi s4, s4, 0x0020 254 | and s4, s4, t3 255 | addi s2, s2, -1 256 | bnez s2, mandelbrot_for_x 257 | 258 | addi s5, s5, 0x0020 259 | and s5, s5, t3 260 | addi s3, s3, -1 261 | bnez s3, mandelbrot_for_y 262 | 263 | jalr zero, s1, 0 264 | 265 | ;; lcd_send_cmd(a0) 266 | lcd_send_cmd: 267 | li t0, LCD_RES 268 | sw t0, SPI_IO(gp) 269 | sw a0, SPI_TX(gp) 270 | li t0, SPI_START 271 | sw t0, SPI_CTL(gp) 272 | lcd_send_cmd_wait: 273 | lw t0, SPI_CTL(gp) 274 | andi t0, t0, SPI_BUSY 275 | bnez t0, lcd_send_cmd_wait 276 | li t0, LCD_CS | LCD_RES 277 | sw t0, SPI_IO(gp) 278 | ret 279 | 280 | ;; lcd_send_data(a0) 281 | lcd_send_data: 282 | li t0, LCD_DC | LCD_RES 283 | sw t0, SPI_IO(gp) 284 | sw a0, SPI_TX(gp) 285 | li t0, SPI_16 | SPI_START 286 | sw t0, SPI_CTL(gp) 287 | lcd_send_data_wait: 288 | lw t0, SPI_CTL(gp) 289 | andi t0, t0, SPI_BUSY 290 | bnez t0, lcd_send_data_wait 291 | li t0, LCD_CS | LCD_RES 292 | sw t0, SPI_IO(gp) 293 | ret 294 | 295 | delay: 296 | li t0, 65536 297 | delay_loop: 298 | addi t0, t0, -1 299 | bnez t0, delay_loop 300 | ret 301 | 302 | ;; colors is referenced by address instead of an offset, which makes this 303 | ;; program not relocatable. 304 | colors: 305 | dc16 0x0000 306 | dc16 0x000c 307 | dc16 0x0013 308 | dc16 0x0015 309 | dc16 0x0195 310 | dc16 0x0335 311 | dc16 0x04d5 312 | dc16 0x34c0 313 | dc16 0x64c0 314 | dc16 0x9cc0 315 | dc16 0x6320 316 | dc16 0xa980 317 | dc16 0xaaa0 318 | dc16 0xcaa0 319 | dc16 0xe980 320 | dc16 0xf800 321 | 322 | -------------------------------------------------------------------------------- /src/peripherals.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2025 by Michael Kohn 9 | 10 | module peripherals 11 | ( 12 | input enable, 13 | input [7:0] address, 14 | input [31:0] data_in, 15 | output reg [31:0] data_out, 16 | //output [7:0] debug, 17 | input write_enable, 18 | //input clk, 19 | input raw_clk, 20 | output speaker_p, 21 | output speaker_m, 22 | output ioport_0, 23 | output ioport_1, 24 | output ioport_2, 25 | output ioport_3, 26 | input button_0, 27 | input reset, 28 | output spi_clk, 29 | output spi_mosi, 30 | input spi_miso, 31 | output uart_tx_0, 32 | input uart_rx_0 33 | ); 34 | 35 | reg [7:0] storage [3:0]; 36 | 37 | reg [15:0] speaker_value_high; 38 | reg [15:0] speaker_value_curr; 39 | reg [7:0] buttons; 40 | 41 | reg speaker_toggle; 42 | reg speaker_value_p; 43 | reg speaker_value_m; 44 | assign speaker_p = speaker_value_p; 45 | assign speaker_m = speaker_value_m; 46 | 47 | reg [7:0] ioport_a = 0; 48 | assign ioport_0 = ioport_a[0]; 49 | reg [7:0] ioport_b = 0; // 8'hf0; 50 | assign ioport_1 = ioport_b[0]; 51 | assign ioport_2 = ioport_b[1]; 52 | assign ioport_3 = ioport_b[2]; 53 | 54 | //assign debug = ioport_b; 55 | //assign debug = spi_tx_buffer; 56 | 57 | wire [7:0] spi_rx_buffer; 58 | reg [15:0] spi_tx_buffer; 59 | wire spi_busy; 60 | reg spi_start; 61 | reg spi_width_16; 62 | 63 | // UART 0. 64 | wire tx_busy; 65 | reg tx_strobe = 0; 66 | reg [7:0] tx_data; 67 | wire [7:0] rx_data; 68 | wire rx_ready; 69 | reg rx_ready_clear = 0; 70 | 71 | /* 72 | reg [15:0] mandelbrot_r; 73 | reg [15:0] mandelbrot_i; 74 | wire mandelbrot_busy; 75 | reg mandelbrot_start; 76 | wire [3:0] mandelbrot_result; 77 | */ 78 | 79 | always @(button_0) begin 80 | buttons = { 7'b0, ~button_0 }; 81 | end 82 | 83 | always @(posedge raw_clk) begin 84 | if (speaker_value_high == 16'b0) begin 85 | speaker_value_curr <= 0; 86 | speaker_value_p <= 0; 87 | speaker_value_m <= 0; 88 | end else begin 89 | speaker_value_curr <= speaker_value_curr + 1'b1; 90 | 91 | if (speaker_value_curr == speaker_value_high) begin 92 | speaker_value_curr <= 0; 93 | speaker_toggle <= ~speaker_toggle; 94 | 95 | speaker_value_p <= speaker_toggle; 96 | speaker_value_m <= ~speaker_toggle; 97 | end 98 | end 99 | end 100 | 101 | // FIXME: Fix this... 102 | // This should be able to be clk instead of raw_clk, but it seems that 103 | // two consecutive writes this module keeps stale data in data_in. So 104 | // will put 6 into both 0x4008 and 0x400a 105 | // Wiring to RAM in between keeps data_in with the correct result. 106 | always @(posedge raw_clk) begin 107 | //always @(posedge enable) begin 108 | if (reset) speaker_value_high <= 0; 109 | 110 | if (write_enable) begin 111 | case (address[7:2]) 112 | 5'h1: spi_tx_buffer <= data_in; 113 | 5'h3: 114 | begin 115 | if (data_in[1] == 1) spi_start <= 1; 116 | spi_width_16 <= data_in[2]; 117 | end 118 | 5'h8: ioport_a <= data_in; 119 | 5'h9: 120 | begin 121 | case (data_in[7:0]) 122 | 60: speaker_value_high <= 45866; // C4 261.63 123 | 61: speaker_value_high <= 43293; // C#4 277.18 124 | 62: speaker_value_high <= 40863; // D4 293.66 125 | 63: speaker_value_high <= 38569; // D#4 311.13 126 | 64: speaker_value_high <= 36404; // E4 329.63 127 | 65: speaker_value_high <= 34361; // F4 349.23 128 | 66: speaker_value_high <= 32433; // F#4 369.99 129 | 67: speaker_value_high <= 30612; // G4 392.00 130 | 68: speaker_value_high <= 28894; // G#4 415.30 131 | 69: speaker_value_high <= 27272; // A4 440.00 132 | 70: speaker_value_high <= 25742; // A#4 466.16 133 | 71: speaker_value_high <= 24297; // B4 493.88 134 | 72: speaker_value_high <= 22933; // C5 523.25 135 | 73: speaker_value_high <= 21646; // C#5 554.37 136 | 74: speaker_value_high <= 20431; // D5 587.33 137 | 75: speaker_value_high <= 19284; // D#5 622.25 138 | 76: speaker_value_high <= 18202; // E5 659.26 139 | 77: speaker_value_high <= 17180; // F5 698.46 140 | 78: speaker_value_high <= 16216; // F#5 739.99 141 | 79: speaker_value_high <= 15306; // G5 783.99 142 | 80: speaker_value_high <= 14447; // G#5 830.61 143 | 81: speaker_value_high <= 13636; // A5 880.00 144 | 82: speaker_value_high <= 12870; // A#5 932.33 145 | 83: speaker_value_high <= 12148; // B5 987.77 146 | 84: speaker_value_high <= 11466; // C6 1046.50 147 | 85: speaker_value_high <= 10823; // C#6 1108.73 148 | 86: speaker_value_high <= 10215; // D6 1174.66 149 | 87: speaker_value_high <= 9642; // D#6 1244.51 150 | 88: speaker_value_high <= 9101; // E6 1318.51 151 | 89: speaker_value_high <= 8590; // F6 1396.91 152 | 90: speaker_value_high <= 8108; // F#6 1479.98 153 | 91: speaker_value_high <= 7653; // G6 1567.98 154 | 92: speaker_value_high <= 7223; // G#6 1661.22 155 | 93: speaker_value_high <= 6818; // A6 1760.00 156 | 94: speaker_value_high <= 6435; // A#6 1864.66 157 | 95: speaker_value_high <= 6074; // B6 1975.53 158 | 96: speaker_value_high <= 5733; // C7 2093.00 159 | default: speaker_value_high <= 0; 160 | endcase 161 | end 162 | 5'ha: ioport_b <= data_in; 163 | //5'hb: mandelbrot_r <= data_in; 164 | //5'hc: mandelbrot_i <= data_in; 165 | //5'hd: if (data_in[1] == 1) mandelbrot_start <= 1; 166 | 5'he: begin tx_data <= data_in; tx_strobe <= 1; end 167 | endcase 168 | end else begin 169 | if (spi_start && spi_busy) spi_start <= 0; 170 | if (tx_strobe && tx_busy) tx_strobe <= 0; 171 | //if (mandelbrot_start && mandelbrot_busy) mandelbrot_start <= 0; 172 | 173 | if (rx_ready_clear == 1) rx_ready_clear <= 0; 174 | 175 | if (enable) begin 176 | case (address[7:2]) 177 | 5'h0: data_out <= buttons; 178 | 5'h1: data_out <= spi_tx_buffer; 179 | 5'h2: data_out <= spi_rx_buffer; 180 | 5'h3: data_out <= { 5'b00000, spi_width_16, 1'b0, spi_busy }; 181 | 5'h8: data_out <= ioport_a; 182 | 5'ha: data_out <= ioport_b; 183 | //5'hb: data_out <= mandelbrot_r; 184 | //5'hc: data_out <= mandelbrot_i; 185 | //5'hd: data_out <= { 7'b0000000, mandelbrot_busy }; 186 | //5'he: data_out <= { 12'b00000000000, mandelbrot_result }; 187 | 5'hf: begin data_out <= rx_data; rx_ready_clear <= 1; end 188 | 5'h10: data_out <= { rx_ready, tx_busy }; 189 | default: data_out <= 0; 190 | endcase 191 | end 192 | end 193 | end 194 | 195 | spi spi_0 196 | ( 197 | .raw_clk (raw_clk), 198 | .start (spi_start), 199 | .width_16 (spi_width_16), 200 | .data_tx (spi_tx_buffer), 201 | .data_rx (spi_rx_buffer), 202 | .busy (spi_busy), 203 | .sclk (spi_clk), 204 | .mosi (spi_mosi), 205 | .miso (spi_miso) 206 | ); 207 | 208 | uart uart_0 209 | ( 210 | .raw_clk (raw_clk), 211 | .tx_data (tx_data), 212 | .tx_strobe (tx_strobe), 213 | .tx_busy (tx_busy), 214 | .tx_pin (uart_tx_0), 215 | .rx_data (rx_data), 216 | .rx_ready (rx_ready), 217 | .rx_ready_clear (rx_ready_clear), 218 | .rx_pin (uart_rx_0) 219 | ); 220 | 221 | endmodule 222 | 223 | -------------------------------------------------------------------------------- /test/lcd_mandel_hw.asm: -------------------------------------------------------------------------------- 1 | .riscv 2 | 3 | ;; Set to 0xc000 for eeprom. 4 | ;.org 0xc000 5 | .org 0x4000 6 | 7 | ;; Registers. 8 | BUTTON equ 0x00 9 | SPI_TX equ 0x04 10 | SPI_RX equ 0x08 11 | SPI_CTL equ 0x0c 12 | PORT0 equ 0x20 13 | SOUND equ 0x24 14 | SPI_IO equ 0x28 15 | 16 | ;; Bits in SPI_CTL. 17 | SPI_BUSY equ 0x01 18 | SPI_START equ 0x02 19 | SPI_16 equ 0x04 20 | 21 | ;; Bits in SPI_IO. 22 | LCD_RES equ 0x01 23 | LCD_DC equ 0x02 24 | LCD_CS equ 0x04 25 | 26 | ;; Bits in PORT0 27 | LED0 equ 0x01 28 | 29 | COMMAND_DISPLAY_OFF equ 0xae 30 | COMMAND_SET_REMAP equ 0xa0 31 | COMMAND_START_LINE equ 0xa1 32 | COMMAND_DISPLAY_OFFSET equ 0xa2 33 | COMMAND_NORMAL_DISPLAY equ 0xa4 34 | COMMAND_SET_MULTIPLEX equ 0xa8 35 | COMMAND_SET_MASTER equ 0xad 36 | COMMAND_POWER_MODE equ 0xb0 37 | COMMAND_PRECHARGE equ 0xb1 38 | COMMAND_CLOCKDIV equ 0xb3 39 | COMMAND_PRECHARGE_A equ 0x8a 40 | COMMAND_PRECHARGE_B equ 0x8b 41 | COMMAND_PRECHARGE_C equ 0x8c 42 | COMMAND_PRECHARGE_LEVEL equ 0xbb 43 | COMMAND_VCOMH equ 0xbe 44 | COMMAND_MASTER_CURRENT equ 0x87 45 | COMMAND_CONTRASTA equ 0x81 46 | COMMAND_CONTRASTB equ 0x82 47 | COMMAND_CONTRASTC equ 0x83 48 | COMMAND_DISPLAY_ON equ 0xaf 49 | 50 | .define mandel mulw 51 | 52 | .macro send_command(value) 53 | li a0, value 54 | jal lcd_send_cmd 55 | .endm 56 | 57 | .macro square_fixed(result, var) 58 | .scope 59 | mv a1, var 60 | li t1, 0x8000 61 | and t1, t1, a1 62 | beqz t1, not_signed 63 | li t1, 0xffff 64 | xor a1, a1, t1 65 | addi a1, a1, 1 66 | and a1, a1, t1 67 | not_signed: 68 | mv a2, a1 69 | jal multiply 70 | mv result, a3 71 | .ends 72 | .endm 73 | 74 | .macro multiply_signed(var_0, var_1) 75 | .scope 76 | mv a1, var_0 77 | mv a2, var_1 78 | li t2, 0x0000 79 | li t1, 0x8000 80 | and t1, t1, a1 81 | beqz t1, not_signed_0 82 | xor t2, t2, t1 83 | xor a1, a1, t3 84 | addi a1, a1, 1 85 | and a1, a1, t3 86 | not_signed_0: 87 | li t1, 0x8000 88 | and t1, t1, a2 89 | beqz t1, not_signed_1 90 | xor t2, t2, t1 91 | xor a2, a2, t3 92 | addi a2, a2, 1 93 | and a2, a2, t3 94 | not_signed_1: 95 | jal multiply 96 | beqz t2, dont_add_sign 97 | xor a3, a3, t3 98 | addi a3, a3, 1 99 | and a3, a3, t3 100 | dont_add_sign: 101 | .ends 102 | .endm 103 | 104 | start: 105 | ;; Point gp to peripherals. 106 | li gp, 0x8000 107 | ;; Clear LED. 108 | li t0, 1 109 | sw t0, PORT0(gp) 110 | li s11, 0 111 | 112 | main: 113 | jal lcd_init 114 | jal lcd_clear 115 | ;jal lcd_clear_2 116 | 117 | li s2, 0 118 | main_while_1: 119 | lw t0, BUTTON(gp) 120 | andi t0, t0, 1 121 | bnez t0, run 122 | xori s2, s2, 1 123 | sb s2, PORT0(gp) 124 | jal delay 125 | 126 | j main_while_1 127 | 128 | run: 129 | jal ra, lcd_clear_2 130 | xori s11, s11, 1 131 | beqz s11, run_hw 132 | jal ra, mandelbrot 133 | li s2, 1 134 | j main_while_1 135 | 136 | run_hw: 137 | jal ra, mandelbrot_hw 138 | li s2, 1 139 | j main_while_1 140 | 141 | lcd_init: 142 | mv s1, ra 143 | li t0, LCD_CS 144 | sw t0, SPI_IO(gp) 145 | jal delay 146 | li t0, LCD_CS | LCD_RES 147 | sw t0, SPI_IO(gp) 148 | 149 | send_command(COMMAND_DISPLAY_OFF) 150 | send_command(COMMAND_SET_REMAP) 151 | send_command(0x72) 152 | send_command(COMMAND_START_LINE) 153 | send_command(0x00) 154 | send_command(COMMAND_DISPLAY_OFFSET) 155 | send_command(0x00) 156 | send_command(COMMAND_NORMAL_DISPLAY) 157 | send_command(COMMAND_SET_MULTIPLEX) 158 | send_command(0x3f) 159 | send_command(COMMAND_SET_MASTER) 160 | send_command(0x8e) 161 | send_command(COMMAND_POWER_MODE) 162 | send_command(COMMAND_PRECHARGE) 163 | send_command(0x31) 164 | send_command(COMMAND_CLOCKDIV) 165 | send_command(0xf0) 166 | send_command(COMMAND_PRECHARGE_A) 167 | send_command(0x64) 168 | send_command(COMMAND_PRECHARGE_B) 169 | send_command(0x78) 170 | send_command(COMMAND_PRECHARGE_C) 171 | send_command(0x64) 172 | send_command(COMMAND_PRECHARGE_LEVEL) 173 | send_command(0x3a) 174 | send_command(COMMAND_VCOMH) 175 | send_command(0x3e) 176 | send_command(COMMAND_MASTER_CURRENT) 177 | send_command(0x06) 178 | send_command(COMMAND_CONTRASTA) 179 | send_command(0x91) 180 | send_command(COMMAND_CONTRASTB) 181 | send_command(0x50) 182 | send_command(COMMAND_CONTRASTC) 183 | send_command(0x7d) 184 | send_command(COMMAND_DISPLAY_ON) 185 | jalr zero, s1, 0 186 | 187 | lcd_clear: 188 | mv s1, ra 189 | li t1, 96 * 64 190 | li a0, 0xff0f 191 | lcd_clear_loop: 192 | jal lcd_send_data 193 | addi t1, t1, -1 194 | bnez t1, lcd_clear_loop 195 | jalr zero, s1, 0 196 | 197 | lcd_clear_2: 198 | mv s1, ra 199 | li t1, 96 * 64 200 | li a0, 0xf00f 201 | lcd_clear_loop_2: 202 | jal lcd_send_data 203 | addi t1, t1, -1 204 | bnez t1, lcd_clear_loop_2 205 | jalr zero, s1, 0 206 | 207 | ;; multiply(a1, a2) -> a3 208 | multiply: 209 | ;mv a3, a1 210 | ;srli a3, a3, 1 211 | ;ret 212 | li a3, 0 213 | li t0, 16 214 | multiply_repeat: 215 | andi a4, a1, 1 216 | beqz a4, multiply_ignore_bit 217 | add a3, a3, a2 218 | multiply_ignore_bit: 219 | slli a2, a2, 1 220 | srli a1, a1, 1 221 | addi t0, t0, -1 222 | bnez t0, multiply_repeat 223 | srai a3, a3, 10 224 | li t0, 0xffff 225 | and a3, a3, t0 226 | ret 227 | 228 | mandelbrot: 229 | mv s1, ra 230 | 231 | ;; final int DEC_PLACE = 10; 232 | ;; final int r0 = (-2 << DEC_PLACE); 233 | ;; final int i0 = (-1 << DEC_PLACE); 234 | ;; final int r1 = (1 << DEC_PLACE); 235 | ;; final int i1 = (1 << DEC_PLACE); 236 | ;; final int dx = (r1 - r0) / 96; (0x0020) 237 | ;; final int dy = (i1 - i0) / 64; (0x0020) 238 | 239 | ;; Mask for 16 bit. 240 | li t3, 0xffff 241 | 242 | ;; for (y = 0; y < 64; y++) 243 | li s3, 64 244 | ;; int i = -1 << 10; 245 | li s5, 0xfc00 246 | mandelbrot_for_y: 247 | 248 | ;; for (x = 0; x < 96; x++) 249 | li s2, 96 250 | ;; int r = -2 << 10; 251 | li s4, 0xf800 252 | mandelbrot_for_x: 253 | ;; zr = r; 254 | ;; zi = i; 255 | mv s6, s4 256 | mv s7, s5 257 | 258 | ;; for (int count = 0; count < 15; count++) 259 | li a0, 15 260 | mandelbrot_for_count: 261 | ;; zr2 = (zr * zr) >> DEC_PLACE; 262 | square_fixed(a6, s6) 263 | 264 | ;; zi2 = (zi * zi) >> DEC_PLACE; 265 | square_fixed(a7, s7) 266 | 267 | ;; if (zr2 + zi2 > (4 << DEC_PLACE)) { break; } 268 | ;; cmp does: 4 - (zr2 + zi2).. if it's negative it's bigger than 4. 269 | add a5, a6, a7 270 | li t0, 4 << 10 271 | slt a5, a5, t0 272 | beqz a5, mandelbrot_stop 273 | 274 | ;; tr = zr2 - zi2; 275 | sub a5, a6, a7 276 | and a5, a5, t3 277 | 278 | ;; ti = ((zr * zi * 2) >> DEC_PLACE) << 1; 279 | multiply_signed(s6, s7) 280 | slli a3, a3, 1 281 | ;mv t1, a3 282 | 283 | ;; zr = tr + curr_r; 284 | add s6, a5, s4 285 | and s6, s6, t3 286 | 287 | ;; zi = ti + curr_i; 288 | add s7, a3, s5 289 | and s7, s7, t3 290 | 291 | addi a0, a0, -1 292 | bnez a0, mandelbrot_for_count 293 | mandelbrot_stop: 294 | 295 | slli a0, a0, 1 296 | li t0, colors 297 | add a0, t0, a0 298 | lw a0, 0(a0) 299 | 300 | jal lcd_send_data 301 | 302 | addi s4, s4, 0x0020 303 | and s4, s4, t3 304 | addi s2, s2, -1 305 | bnez s2, mandelbrot_for_x 306 | 307 | addi s5, s5, 0x0020 308 | and s5, s5, t3 309 | addi s3, s3, -1 310 | bnez s3, mandelbrot_for_y 311 | 312 | jalr zero, s1, 0 313 | 314 | mandelbrot_hw: 315 | mv s1, ra 316 | 317 | ;; final int DEC_PLACE = 10; 318 | ;; final int r0 = (-2 << DEC_PLACE); 319 | ;; final int i0 = (-1 << DEC_PLACE); 320 | ;; final int r1 = (1 << DEC_PLACE); 321 | ;; final int i1 = (1 << DEC_PLACE); 322 | ;; final int dx = (r1 - r0) / 96; (0x0020) 323 | ;; final int dy = (i1 - i0) / 64; (0x0020) 324 | 325 | ;; Mask for 16 bit. 326 | li t3, 0xffff 327 | 328 | ;; for (y = 0; y < 64; y++) 329 | li s3, 64 330 | ;; int i = -1 << 10; 331 | li s5, 0xfc00 332 | mandelbrot_hw_for_y: 333 | 334 | ;; for (x = 0; x < 96; x++) 335 | li s2, 96 336 | ;; int r = -2 << 10; 337 | li s4, 0xf800 338 | mandelbrot_hw_for_x: 339 | 340 | mandel a0, s4, s5 341 | 342 | slli a0, a0, 1 343 | li t0, colors 344 | add a0, t0, a0 345 | lh a0, 0(a0) 346 | 347 | jal lcd_send_data 348 | 349 | addi s4, s4, 0x0020 350 | and s4, s4, t3 351 | addi s2, s2, -1 352 | bnez s2, mandelbrot_hw_for_x 353 | 354 | addi s5, s5, 0x0020 355 | and s5, s5, t3 356 | addi s3, s3, -1 357 | bnez s3, mandelbrot_hw_for_y 358 | jalr zero, s1, 0 359 | 360 | ;; lcd_send_cmd(a0) 361 | lcd_send_cmd: 362 | li t0, LCD_RES 363 | sw t0, SPI_IO(gp) 364 | sw a0, SPI_TX(gp) 365 | li t0, SPI_START 366 | sw t0, SPI_CTL(gp) 367 | lcd_send_cmd_wait: 368 | lw t0, SPI_CTL(gp) 369 | andi t0, t0, SPI_BUSY 370 | bnez t0, lcd_send_cmd_wait 371 | li t0, LCD_CS | LCD_RES 372 | sw t0, SPI_IO(gp) 373 | ret 374 | 375 | ;; lcd_send_data(a0) 376 | lcd_send_data: 377 | li t0, LCD_DC | LCD_RES 378 | sw t0, SPI_IO(gp) 379 | sw a0, SPI_TX(gp) 380 | li t0, SPI_16 | SPI_START 381 | sw t0, SPI_CTL(gp) 382 | lcd_send_data_wait: 383 | lw t0, SPI_CTL(gp) 384 | andi t0, t0, SPI_BUSY 385 | bnez t0, lcd_send_data_wait 386 | li t0, LCD_CS | LCD_RES 387 | sw t0, SPI_IO(gp) 388 | ret 389 | 390 | delay: 391 | li t0, 65536 392 | delay_loop: 393 | addi t0, t0, -1 394 | bnez t0, delay_loop 395 | ret 396 | 397 | ;; colors is referenced by address instead of an offset, which makes this 398 | ;; program not relocatable. 399 | colors: 400 | dc16 0x0000 401 | dc16 0x000c 402 | dc16 0x0013 403 | dc16 0x0015 404 | dc16 0x0195 405 | dc16 0x0335 406 | dc16 0x04d5 407 | dc16 0x34c0 408 | dc16 0x64c0 409 | dc16 0x9cc0 410 | dc16 0x6320 411 | dc16 0xa980 412 | dc16 0xaaa0 413 | dc16 0xcaa0 414 | dc16 0xe980 415 | dc16 0xf800 416 | 417 | -------------------------------------------------------------------------------- /src/riscv.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2025 by Michael Kohn 9 | 10 | `include "alu.vinc" 11 | 12 | module riscv 13 | ( 14 | `ifndef TANG_NANO 15 | output [7:0] leds, 16 | output [3:0] column, 17 | `endif 18 | input raw_clk, 19 | output speaker_p, 20 | output speaker_m, 21 | output ioport_0, 22 | output ioport_1, 23 | output ioport_2, 24 | output ioport_3, 25 | input button_reset, 26 | input button_halt, 27 | input button_program_select, 28 | input button_0, 29 | output spi_clk, 30 | output spi_mosi, 31 | input spi_miso, 32 | output uart_tx_0, 33 | input uart_rx_0 34 | ); 35 | 36 | `ifndef TANG_NANO 37 | // iceFUN 8x4 LEDs used for debugging. 38 | reg [7:0] leds_value; 39 | reg [3:0] column_value; 40 | 41 | assign leds = leds_value; 42 | assign column = column_value; 43 | `endif 44 | 45 | // Memory bus (ROM, RAM, peripherals). 46 | reg [15:0] mem_address = 0; 47 | reg [31:0] mem_write = 0; 48 | reg [3:0] mem_write_mask = 0; 49 | wire [31:0] mem_read; 50 | //wire mem_data_ready; 51 | reg mem_bus_enable = 0; 52 | reg mem_write_enable = 0; 53 | 54 | //wire [7:0] mem_debug; 55 | 56 | // Clock. 57 | reg [21:0] count = 0; 58 | reg [3:0] clock_div; 59 | reg [14:0] delay_loop; 60 | wire clk; 61 | assign clk = clock_div[0]; 62 | 63 | // Registers. 64 | reg [31:0] registers [31:0]; 65 | reg [15:0] pc = 0; 66 | reg [15:0] pc_current = 0; 67 | 68 | // Instruction 69 | reg [31:0] instruction; 70 | wire [3:0] op; 71 | wire [2:0] op_lo; 72 | wire [4:0] rd; 73 | wire [4:0] rs1; 74 | wire [4:0] rs2; 75 | wire [4:0] shamt; 76 | wire [2:0] funct3; 77 | wire [6:0] funct7; 78 | wire signed [12:0] branch_offset; 79 | wire [2:0] memory_size; 80 | assign op = instruction[6:3]; 81 | assign op_lo = instruction[2:0]; 82 | assign rd = instruction[11:7]; 83 | assign rs1 = instruction[19:15]; 84 | assign rs2 = instruction[24:20]; 85 | assign shamt = instruction[24:20]; 86 | assign funct3 = instruction[14:12]; 87 | assign funct7 = instruction[31:25]; 88 | 89 | assign branch_offset = { 90 | instruction[31], 91 | instruction[7], 92 | instruction[30:25], 93 | instruction[11:8], 94 | 1'b0 95 | }; 96 | 97 | wire [31:0] upper_immediate; 98 | assign upper_immediate = { instruction[31:12], 12'h000 }; 99 | 100 | wire [11:0] uimm; 101 | wire signed [11:0] simm; 102 | assign uimm = instruction[31:20]; 103 | assign simm = instruction[31:20]; 104 | 105 | wire signed [11:0] st_offset; 106 | assign st_offset = { funct7, instruction[11:7] }; 107 | 108 | wire signed [11:0] ls_offset; 109 | assign ls_offset = op[2] == 0 ? simm : st_offset; 110 | 111 | wire [15:0] branch_address; 112 | assign branch_address = $signed(pc_current) + branch_offset; 113 | reg do_branch; 114 | 115 | reg [31:0] source; 116 | reg [31:0] result; 117 | reg [31:0] arg_1; 118 | wire [31:0] alu_result; 119 | 120 | wire [2:0] alu_op; 121 | assign alu_op = funct3; 122 | reg is_alt; 123 | reg is_alu; 124 | reg [1:0] wb; 125 | 126 | wire [31:0] wb_result; 127 | assign wb_result = is_alu ? alu_result : result; 128 | 129 | // Load / Store. 130 | assign memory_size = instruction[14:12]; 131 | //reg [31:0] ea; 132 | reg [15:0] ea; 133 | 134 | // This block is simply a clock divider for the raw_clk. 135 | always @(posedge raw_clk) begin 136 | count <= count + 1; 137 | clock_div <= clock_div + 1; 138 | end 139 | 140 | `ifndef TANG_NANO 141 | // Debug: This block simply drives the 8x4 LEDs. 142 | always @(posedge raw_clk) begin 143 | case (count[9:7]) 144 | 3'b000: begin column_value <= 4'b0111; leds_value <= ~registers[6][7:0]; end 145 | 3'b010: begin column_value <= 4'b1011; leds_value <= ~registers[6][15:8]; end 146 | //3'b010: begin column_value <= 4'b1011; leds_value <= ~instruction[7:0]; end 147 | //3'b010: begin column_value <= 4'b1011; leds_value <= ~pc[15:8]; end 148 | 3'b100: begin column_value <= 4'b1101; leds_value <= ~pc[7:0]; end 149 | 3'b110: begin column_value <= 4'b1110; leds_value <= ~state; end 150 | default: begin column_value <= 4'b1111; leds_value <= 8'hff; end 151 | endcase 152 | end 153 | `endif 154 | 155 | parameter STATE_RESET = 0; 156 | parameter STATE_DELAY_LOOP = 1; 157 | parameter STATE_FETCH_OP_0 = 2; 158 | parameter STATE_FETCH_OP_1 = 3; 159 | parameter STATE_START_DECODE = 4; 160 | 161 | parameter STATE_FETCH_LOAD = 5; 162 | parameter STATE_STORE = 6; 163 | 164 | parameter STATE_CONTROL = 7; 165 | parameter STATE_LUI = 8; 166 | parameter STATE_ALU_IMM = 9; 167 | parameter STATE_ALU_REG = 10; 168 | 169 | parameter STATE_BRANCH = 11; 170 | parameter STATE_WRITEBACK = 12; 171 | 172 | parameter STATE_DEBUG = 13; 173 | parameter STATE_ERROR = 14; 174 | parameter STATE_HALTED = 15; 175 | 176 | reg [3:0] state = STATE_RESET; 177 | 178 | parameter WB_NONE = 0; 179 | parameter WB_RD = 1; 180 | parameter WB_PC = 2; 181 | 182 | /* 183 | function signed [31:0] sign12(input signed [11:0] data); 184 | sign12 = data; 185 | endfunction 186 | */ 187 | 188 | //`define sign_imm12(data) { {20{ data[31] }}, data[31:20] } 189 | 190 | // This block is the main CPU instruction execute state machine. 191 | always @(posedge clk) begin 192 | if (!button_reset) 193 | state <= STATE_RESET; 194 | else if (!button_halt) 195 | state <= STATE_HALTED; 196 | else 197 | case (state) 198 | STATE_RESET: 199 | begin 200 | mem_address <= 0; 201 | mem_write_enable <= 0; 202 | mem_write <= 0; 203 | delay_loop <= 12000; 204 | state <= STATE_DELAY_LOOP; 205 | end 206 | STATE_DELAY_LOOP: 207 | begin 208 | // This is probably not needed. The chip starts up fine without it. 209 | if (delay_loop == 0) begin 210 | pc <= 16'h4000; 211 | state <= STATE_FETCH_OP_0; 212 | end else begin 213 | delay_loop <= delay_loop - 1; 214 | end 215 | end 216 | STATE_FETCH_OP_0: 217 | begin 218 | registers[0] <= 0; 219 | wb <= WB_RD; 220 | is_alt <= 0; 221 | is_alu <= 0; 222 | do_branch <= 0; 223 | mem_bus_enable <= 1; 224 | mem_write_enable <= 0; 225 | mem_address <= pc; 226 | pc_current = pc; 227 | pc <= pc + 4; 228 | state <= STATE_FETCH_OP_1; 229 | end 230 | STATE_FETCH_OP_1: 231 | begin 232 | instruction = mem_read; 233 | //arg_1 = registers[rs2]; 234 | arg_1 = registers[instruction[24:20]]; 235 | mem_bus_enable <= 0; 236 | state <= STATE_START_DECODE; 237 | end 238 | STATE_START_DECODE: 239 | begin 240 | case (op_lo) 241 | 3'b011: 242 | if (op[3] == 0) begin 243 | 244 | // ALU immediate or register. 245 | is_alu <= op[2:0] == 3'b010 || op[2:0] == 3'b110; 246 | 247 | // Load. 248 | mem_bus_enable <= op[2:0] == 3'b000; 249 | 250 | case (op[2:0]) 251 | 3'b000: 252 | begin 253 | // Load. 254 | state <= STATE_FETCH_LOAD; 255 | end 256 | 3'b010: 257 | begin 258 | // ALU Immediate. 259 | state <= STATE_ALU_IMM; 260 | end 261 | 3'b100: 262 | begin 263 | // Store. 264 | state <= STATE_STORE; 265 | end 266 | 3'b110: 267 | begin 268 | // ALU Reg. 269 | state <= STATE_ALU_REG; 270 | end 271 | default: 272 | begin 273 | state <= STATE_ERROR; 274 | end 275 | endcase 276 | end else begin 277 | //state <= op[2:0] == 3'b100 ? STATE_BRANCH : STATE_HALTED; 278 | 279 | if (op[2:0] == 3'b100) 280 | // branch. 281 | state <= STATE_BRANCH; 282 | else 283 | // 3'b110 is ebreak. 284 | state <= STATE_HALTED; 285 | end 286 | 3'b111: 287 | begin 288 | // lui, auipc, jal, jalr 289 | state <= op[3] == 0 ? STATE_LUI : STATE_CONTROL; 290 | end 291 | default: 292 | begin 293 | state <= STATE_ERROR; 294 | end 295 | endcase 296 | 297 | source = registers[rs1]; 298 | ea = source + ls_offset; 299 | mem_address = ea; 300 | end 301 | STATE_FETCH_LOAD: 302 | begin 303 | mem_bus_enable <= 0; 304 | 305 | case (memory_size[1:0]) 306 | 3'b00: 307 | begin 308 | case (ea[1:0]) 309 | 0: 310 | begin 311 | result[7:0] <= mem_read[7:0]; 312 | result[31:8] <= { {24{ mem_read[7] & ~memory_size[2] } } }; 313 | end 314 | 1: 315 | begin 316 | result[7:0] <= mem_read[15:8]; 317 | result[31:8] <= { {24{ mem_read[15] & ~memory_size[2] } } }; 318 | end 319 | 2: 320 | begin 321 | result[7:0] <= mem_read[23:16]; 322 | result[31:8] <= { {24{ mem_read[23] & ~memory_size[2] } } }; 323 | end 324 | 3: 325 | begin 326 | result[7:0] <= mem_read[31:24]; 327 | result[31:8] <= { {24{ mem_read[31] & ~memory_size[2] } } }; 328 | end 329 | endcase 330 | end 331 | 3'b01: 332 | begin 333 | case (ea[1]) 334 | 0: 335 | begin 336 | result[15:0] <= mem_read[15:0]; 337 | result[31:16] <= { {16{ mem_read[15] & ~memory_size[2] } } }; 338 | end 339 | 1: 340 | begin 341 | result[15:0] <= mem_read[31:16]; 342 | result[31:16] <= { {16{ mem_read[31] & ~memory_size[2] } } }; 343 | end 344 | endcase 345 | end 346 | 3'b10: 347 | begin 348 | result <= mem_read; 349 | end 350 | endcase 351 | 352 | state <= STATE_WRITEBACK; 353 | end 354 | STATE_STORE: 355 | begin 356 | case (funct3) 357 | 3'b000: 358 | begin 359 | mem_write[7:0] <= arg_1[7:0]; 360 | mem_write[15:8] <= arg_1[7:0]; 361 | mem_write[23:16] <= arg_1[7:0]; 362 | mem_write[31:24] <= arg_1[7:0]; 363 | 364 | mem_write_mask[0] <= ~(ea[1:0] == 0); 365 | mem_write_mask[1] <= ~(ea[1:0] == 1); 366 | mem_write_mask[2] <= ~(ea[1:0] == 2); 367 | mem_write_mask[3] <= ~(ea[1:0] == 3); 368 | end 369 | 3'b001: 370 | begin 371 | mem_write[15:0] <= arg_1[15:0]; 372 | mem_write[31:16] <= arg_1[15:0]; 373 | 374 | mem_write_mask[0] <= ea[1:0] == 2; 375 | mem_write_mask[1] <= ea[1:0] == 2; 376 | mem_write_mask[2] <= ea[1:0] == 0; 377 | mem_write_mask[3] <= ea[1:0] == 0; 378 | end 379 | 3'b010: 380 | begin 381 | mem_write <= arg_1; 382 | mem_write_mask <= 4'b0000; 383 | end 384 | endcase 385 | 386 | wb <= WB_NONE; 387 | mem_write_enable <= 1; 388 | mem_bus_enable <= 1; 389 | state <= STATE_WRITEBACK; 390 | end 391 | STATE_CONTROL: 392 | begin 393 | if (op[0] == 0) 394 | // jalr. 395 | result <= ($signed(source) + simm) & 16'hfffc; 396 | else 397 | // jal. 398 | result <= $signed(pc_current) + $signed( { 399 | instruction[31], 400 | instruction[19:12], 401 | instruction[20], 402 | instruction[30:21], 403 | 1'b0 404 | } ); 405 | 406 | registers[rd] <= pc; 407 | wb <= WB_PC; 408 | do_branch <= 1; 409 | state <= STATE_WRITEBACK; 410 | end 411 | STATE_LUI: 412 | begin 413 | if (op[2] == 0) begin 414 | // auipc. 415 | result <= pc_current + { instruction[31:12], 12'b0 }; 416 | end else begin 417 | // lui. 418 | result <= { instruction[31:12], 12'h000 }; 419 | end 420 | 421 | state <= STATE_WRITEBACK; 422 | end 423 | STATE_ALU_IMM: 424 | begin 425 | // ALU immediate. 426 | if (alu_op == ALU_OP_SLTU) 427 | arg_1 <= uimm; 428 | else if (alu_op == ALU_OP_SLL || alu_op == ALU_OP_SRL) 429 | arg_1 <= shamt; 430 | else 431 | arg_1 <= simm; 432 | 433 | is_alt <= funct7[5] && funct3 == ALU_OP_SRL; 434 | state <= STATE_WRITEBACK; 435 | end 436 | STATE_ALU_REG: 437 | begin 438 | // ALU reg, reg. 439 | //arg_1 <= registers[rs2]; 440 | is_alt <= funct7[5]; 441 | state <= STATE_WRITEBACK; 442 | end 443 | STATE_BRANCH: 444 | begin 445 | /* 446 | // This doesn't seem to lower the LUT count. 447 | 448 | do_branch <= 449 | funct3 == 3'b000 && source == arg_1 || 450 | funct3 == 3'b001 && source != arg_1 || 451 | funct3 == 3'b100 && $signed(source) < $signed(arg_1) || 452 | funct3 == 3'b101 && $signed(source) >= $signed(arg_1) || 453 | funct3 == 3'b110 && source < arg_1 || 454 | funct3 == 3'b111 && source >= arg_1; 455 | */ 456 | 457 | case (funct3) 458 | 3'b000: 459 | // beq. 460 | do_branch <= source == arg_1; 461 | 3'b001: 462 | // bne. 463 | do_branch <= source != arg_1; 464 | 3'b100: 465 | // blt. 466 | do_branch <= $signed(source) < $signed(arg_1); 467 | 3'b101: 468 | // bge. 469 | do_branch <= $signed(source) >= $signed(arg_1); 470 | 3'b110: 471 | // bltu. 472 | do_branch <= source < arg_1; 473 | 3'b111: 474 | // bgeu. 475 | do_branch <= source >= arg_1; 476 | endcase 477 | 478 | result <= branch_address; 479 | wb <= WB_PC; 480 | 481 | state <= STATE_WRITEBACK; 482 | end 483 | STATE_WRITEBACK: 484 | begin 485 | if (wb == WB_RD) registers[rd] <= wb_result; 486 | if (wb == WB_PC) if (do_branch) pc <= result[15:0]; 487 | 488 | mem_bus_enable <= 0; 489 | mem_write_enable <= 0; 490 | 491 | state <= STATE_FETCH_OP_0; 492 | end 493 | STATE_DEBUG: 494 | begin 495 | state <= STATE_DEBUG; 496 | end 497 | STATE_ERROR: 498 | begin 499 | state <= STATE_ERROR; 500 | end 501 | STATE_HALTED: 502 | begin 503 | state <= STATE_HALTED; 504 | end 505 | endcase 506 | end 507 | 508 | memory_bus memory_bus_0( 509 | .address (mem_address), 510 | .data_in (mem_write), 511 | .write_mask (mem_write_mask), 512 | .data_out (mem_read), 513 | //.debug (mem_debug), 514 | //.data_ready (mem_data_ready), 515 | .bus_enable (mem_bus_enable), 516 | .write_enable (mem_write_enable), 517 | //.clk (clk), 518 | .raw_clk (raw_clk), 519 | .speaker_p (speaker_p), 520 | .speaker_m (speaker_m), 521 | .ioport_0 (ioport_0), 522 | .ioport_1 (ioport_1), 523 | .ioport_2 (ioport_2), 524 | .ioport_3 (ioport_3), 525 | .button_0 (button_0), 526 | .reset (~button_reset), 527 | .spi_clk (spi_clk), 528 | .spi_mosi (spi_mosi), 529 | .spi_miso (spi_miso), 530 | .uart_tx_0 (uart_tx_0), 531 | .uart_rx_0 (uart_rx_0) 532 | ); 533 | 534 | alu alu_0( 535 | .source (source), 536 | .arg_1 (arg_1), 537 | .alu_op (alu_op), 538 | .is_alt (is_alt), 539 | .result (alu_result), 540 | ); 541 | 542 | endmodule 543 | 544 | -------------------------------------------------------------------------------- /src/riscv_mandel.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2025 by Michael Kohn 9 | 10 | module riscv 11 | ( 12 | output [7:0] leds, 13 | output [3:0] column, 14 | input raw_clk, 15 | output eeprom_cs, 16 | output eeprom_clk, 17 | output eeprom_di, 18 | input eeprom_do, 19 | output speaker_p, 20 | output speaker_m, 21 | output ioport_0, 22 | output ioport_1, 23 | output ioport_2, 24 | output ioport_3, 25 | input button_reset, 26 | input button_halt, 27 | input button_program_select, 28 | input button_0, 29 | output spi_clk, 30 | output spi_mosi, 31 | input spi_miso 32 | ); 33 | 34 | // iceFUN 8x4 LEDs used for debugging. 35 | reg [7:0] leds_value; 36 | reg [3:0] column_value; 37 | 38 | assign leds = leds_value; 39 | assign column = column_value; 40 | 41 | // Memory bus (ROM, RAM, peripherals). 42 | reg [15:0] mem_address = 0; 43 | reg [31:0] mem_write = 0; 44 | reg [3:0] mem_write_mask = 0; 45 | wire [31:0] mem_read; 46 | //wire mem_data_ready; 47 | reg mem_bus_enable = 0; 48 | reg mem_write_enable = 0; 49 | 50 | //wire [7:0] mem_debug; 51 | 52 | // Clock. 53 | reg [21:0] count = 0; 54 | reg [4:0] state = 0; 55 | reg [19:0] clock_div; 56 | reg [14:0] delay_loop; 57 | wire clk; 58 | assign clk = clock_div[0]; 59 | 60 | // Registers. 61 | //wire [31:0] registers [0]; 62 | //assign registers[0] = 0; 63 | reg [31:0] registers [31:0]; 64 | reg [15:0] pc = 0; 65 | reg [15:0] pc_current = 0; 66 | 67 | // Instruction 68 | reg [31:0] instruction; 69 | wire [6:0] op; 70 | wire [4:0] rd; 71 | wire [4:0] rs1; 72 | wire [4:0] rs2; 73 | wire [4:0] shamt; 74 | wire [2:0] funct3; 75 | wire [6:0] funct7; 76 | wire signed [12:0] branch_offset; 77 | wire [2:0] memory_size; 78 | assign op = instruction[6:0]; 79 | assign rd = instruction[11:7]; 80 | assign rs1 = instruction[19:15]; 81 | assign rs2 = instruction[24:20]; 82 | assign shamt = instruction[24:20]; 83 | assign funct3 = instruction[14:12]; 84 | assign funct7 = instruction[31:25]; 85 | assign branch_offset = { 86 | instruction[31], 87 | instruction[7], 88 | instruction[30:25], 89 | instruction[11:8], 90 | 1'b0 91 | }; 92 | 93 | wire [31:0] upper_immediate; 94 | assign upper_immediate = { instruction[31:12], 12'h000 }; 95 | 96 | wire [11:0] uimm; 97 | wire signed [11:0] simm; 98 | assign uimm = instruction[31:20]; 99 | assign simm = instruction[31:20]; 100 | 101 | wire signed [11:0] st_offset; 102 | assign st_offset = { funct7, instruction[11:7] }; 103 | 104 | wire [15:0] branch_address; 105 | assign branch_address = $signed(pc_current) + branch_offset; 106 | reg do_branch; 107 | 108 | reg [31:0] source; 109 | reg [31:0] result; 110 | 111 | // Load / Store. 112 | assign memory_size = instruction[14:12]; 113 | reg [31:0] ea; 114 | //reg [31:0] ea_aligned; 115 | 116 | // Lower 6 its of the instruction. 117 | wire [5:0] opcode; 118 | assign opcode = instruction[5:0]; 119 | 120 | // Eeprom. 121 | reg [10:0] eeprom_count; 122 | wire [7:0] eeprom_data_out; 123 | reg [7:0] eeprom_holding [3:0]; 124 | reg [10:0] eeprom_address; 125 | reg [15:0] eeprom_mem_address; 126 | reg eeprom_strobe = 0; 127 | wire eeprom_ready; 128 | 129 | // Mandelbrot. 130 | reg [15:0] mandelbrot_r; 131 | reg [15:0] mandelbrot_i; 132 | wire mandelbrot_busy; 133 | reg mandelbrot_start = 0; 134 | wire [3:0] mandelbrot_result; 135 | 136 | // Debug. 137 | //reg [7:0] debug_0 = 0; 138 | //reg [7:0] debug_1 = 0; 139 | //reg [7:0] debug_2 = 0; 140 | //reg [7:0] debug_3 = 0; 141 | 142 | // This block is simply a clock divider for the raw_clk. 143 | always @(posedge raw_clk) begin 144 | count <= count + 1; 145 | clock_div <= clock_div + 1; 146 | end 147 | 148 | // Debug: This block simply drives the 8x4 LEDs. 149 | always @(posedge raw_clk) begin 150 | case (count[9:7]) 151 | 3'b000: begin column_value <= 4'b0111; leds_value <= ~registers[6][7:0]; end 152 | 3'b010: begin column_value <= 4'b1011; leds_value <= ~registers[6][15:8]; end 153 | //3'b010: begin column_value <= 4'b1011; leds_value <= ~instruction[7:0]; end 154 | 3'b100: begin column_value <= 4'b1101; leds_value <= ~pc[7:0]; end 155 | 3'b110: begin column_value <= 4'b1110; leds_value <= ~state; end 156 | default: begin column_value <= 4'b1111; leds_value <= 8'hff; end 157 | endcase 158 | end 159 | 160 | parameter STATE_RESET = 0; 161 | parameter STATE_DELAY_LOOP = 1; 162 | parameter STATE_FETCH_OP_0 = 2; 163 | parameter STATE_FETCH_OP_1 = 3; 164 | parameter STATE_START_DECODE = 4; 165 | parameter STATE_EXECUTE_E = 5; 166 | parameter STATE_FETCH_LOAD_1 = 6; 167 | 168 | parameter STATE_STORE_0 = 7; 169 | parameter STATE_STORE_1 = 8; 170 | 171 | parameter STATE_ALU_0 = 9; 172 | parameter STATE_ALU_1 = 10; 173 | 174 | parameter STATE_BRANCH_1 = 11; 175 | parameter STATE_MANDELBROT_1 = 12; 176 | parameter STATE_MANDELBROT_2 = 13; 177 | 178 | parameter STATE_HALTED = 19; 179 | parameter STATE_ERROR = 20; 180 | parameter STATE_DEBUG = 21; 181 | parameter STATE_EEPROM_START = 22; 182 | parameter STATE_EEPROM_READ = 23; 183 | parameter STATE_EEPROM_WAIT = 24; 184 | parameter STATE_EEPROM_WRITE = 25; 185 | parameter STATE_EEPROM_DONE = 26; 186 | 187 | /* 188 | function signed [31:0] sign12(input signed [11:0] data); 189 | sign12 = data; 190 | endfunction 191 | */ 192 | 193 | //`define sign_imm12(data) { {20{ data[31] }}, data[31:20] } 194 | 195 | // This block is the main CPU instruction execute state machine. 196 | always @(posedge clk) begin 197 | if (!button_reset) 198 | state <= STATE_RESET; 199 | else if (!button_halt) 200 | state <= STATE_HALTED; 201 | else 202 | case (state) 203 | STATE_RESET: 204 | begin 205 | mem_address <= 0; 206 | mem_write_enable <= 0; 207 | mem_write <= 0; 208 | instruction <= 0; 209 | delay_loop <= 12000; 210 | //eeprom_strobe <= 0; 211 | mandelbrot_start <= 0; 212 | state <= STATE_DELAY_LOOP; 213 | end 214 | STATE_DELAY_LOOP: 215 | begin 216 | // This is probably not needed. The chip starts up fine without it. 217 | if (delay_loop == 0) begin 218 | 219 | // If button is not pushed, start rom.v code otherwise use EEPROM. 220 | if (button_program_select) begin 221 | pc <= 16'h4000; 222 | state <= STATE_FETCH_OP_0; 223 | end else begin 224 | pc <= 16'hc000; 225 | state <= STATE_EEPROM_START; 226 | end 227 | end else begin 228 | delay_loop <= delay_loop - 1; 229 | end 230 | end 231 | STATE_FETCH_OP_0: 232 | begin 233 | registers[0] <= 0; 234 | mem_bus_enable <= 1; 235 | mem_write_enable <= 0; 236 | mem_address <= pc; 237 | pc_current = pc; 238 | pc <= pc + 4; 239 | state <= STATE_FETCH_OP_1; 240 | end 241 | STATE_FETCH_OP_1: 242 | begin 243 | mem_bus_enable <= 0; 244 | instruction <= mem_read; 245 | state <= STATE_START_DECODE; 246 | end 247 | STATE_START_DECODE: 248 | begin 249 | case (op) 250 | 7'b0110111: 251 | begin 252 | // lui. 253 | registers[rd] <= { instruction[31:12], 12'h000 }; 254 | state <= STATE_FETCH_OP_0; 255 | end 256 | 7'b0010111: 257 | begin 258 | // auipc. 259 | registers[rd] <= pc_current + { instruction[31:12], 12'b0 }; 260 | state <= STATE_FETCH_OP_0; 261 | end 262 | 7'b0111011: 263 | begin 264 | // mandel rd, rs1, rs2 (aka mulw) 265 | if (funct7 == 7'b0000001 && funct3 == 3'b000) begin 266 | mandelbrot_r <= registers[rs1]; 267 | state <= STATE_MANDELBROT_1; 268 | end 269 | end 270 | 7'b1101111: 271 | begin 272 | // jal. 273 | registers[rd] <= pc; 274 | 275 | pc <= $signed(pc_current) + $signed( { 276 | instruction[31], 277 | instruction[19:12], 278 | instruction[20], 279 | instruction[30:21], 280 | 1'b0 281 | } ); 282 | 283 | state <= STATE_FETCH_OP_0; 284 | end 285 | 7'b1100111: 286 | begin 287 | // jalr. 288 | pc <= ($signed(registers[rs1]) + simm) & 16'hfffc; 289 | registers[rd] <= pc; 290 | state <= STATE_FETCH_OP_0; 291 | end 292 | 7'b1100011: 293 | begin 294 | // branch. 295 | source <= registers[rs2]; 296 | state <= STATE_BRANCH_1; 297 | end 298 | 7'b0000011: 299 | begin 300 | // Load. 301 | ea <= registers[rs1] + simm; 302 | mem_bus_enable <= 1; 303 | mem_write_enable <= 0; 304 | mem_address <= registers[rs1] + simm; 305 | state <= STATE_FETCH_LOAD_1; 306 | end 307 | 7'b0100011: 308 | begin 309 | // Store. 310 | ea <= registers[rs1] + st_offset; 311 | mem_address <= registers[rs1] + st_offset; 312 | mem_bus_enable <= 0; 313 | state <= STATE_STORE_0; 314 | end 315 | 7'b0010011: 316 | begin 317 | // ALU immediate. 318 | case (funct3) 319 | 3'b000: result <= $signed(registers[rs1]) + simm; 320 | 3'b010: result <= $signed(registers[rs1]) < simm; 321 | 3'b011: result <= $signed(registers[rs1]) < uimm; 322 | 3'b100: result <= registers[rs1] ^ simm; 323 | 3'b110: result <= registers[rs1] | simm; 324 | 3'b111: result <= registers[rs1] & simm; 325 | // Shift. 326 | 3'b001: result <= registers[rs1] << shamt; 327 | 3'b101: 328 | if (funct7 == 0) 329 | result <= registers[rs1] >> shamt; 330 | else 331 | result <= $signed(registers[rs1]) >>> shamt; 332 | endcase 333 | 334 | state <= STATE_ALU_1; 335 | end 336 | 7'b0110011: 337 | begin 338 | // ALU reg, reg. 339 | source <= registers[rs2]; 340 | state <= STATE_ALU_0; 341 | end 342 | 7'b1110011: 343 | begin 344 | state <= STATE_EXECUTE_E; 345 | end 346 | default 347 | begin 348 | state <= STATE_ERROR; 349 | end 350 | endcase 351 | end 352 | STATE_EXECUTE_E: 353 | begin 354 | // Since this core only supports "ebreak", send all instructions 355 | // to the halted state. 356 | state <= STATE_HALTED; 357 | end 358 | STATE_FETCH_LOAD_1: 359 | begin 360 | mem_bus_enable <= 0; 361 | 362 | case (memory_size[1:0]) 363 | 3'b00: 364 | begin 365 | case (ea[1:0]) 366 | 0: 367 | begin 368 | registers[rd][7:0] <= mem_read[7:0]; 369 | registers[rd][31:8] <= { {24{ mem_read[7] & ~memory_size[2] } } }; 370 | end 371 | 1: 372 | begin 373 | registers[rd][7:0] <= mem_read[15:8]; 374 | registers[rd][31:8] <= { {24{ mem_read[15] & ~memory_size[2] } } }; 375 | end 376 | 2: 377 | begin 378 | registers[rd][7:0] <= mem_read[23:16]; 379 | registers[rd][31:8] <= { {24{ mem_read[23] & ~memory_size[2] } } }; 380 | end 381 | 3: 382 | begin 383 | registers[rd][7:0] <= mem_read[31:24]; 384 | registers[rd][31:8] <= { {24{ mem_read[31] & ~memory_size[2] } } }; 385 | end 386 | endcase 387 | end 388 | 3'b01: 389 | begin 390 | case (ea[1]) 391 | 0: 392 | begin 393 | registers[rd][15:0] <= mem_read[15:0]; 394 | registers[rd][31:16] <= { {16{ mem_read[15] & ~memory_size[2] } } }; 395 | end 396 | 1: 397 | begin 398 | registers[rd][15:0] <= mem_read[31:16]; 399 | registers[rd][31:16] <= { {16{ mem_read[31] & ~memory_size[2] } } }; 400 | end 401 | endcase 402 | end 403 | 3'b10: 404 | begin 405 | registers[rd] <= mem_read; 406 | end 407 | endcase 408 | 409 | state <= STATE_FETCH_OP_0; 410 | end 411 | STATE_STORE_0: 412 | begin 413 | case (funct3) 414 | 3'b000: 415 | begin 416 | mem_write[7:0] <= registers[rs2][7:0]; 417 | mem_write[15:8] <= registers[rs2][7:0]; 418 | mem_write[23:16] <= registers[rs2][7:0]; 419 | mem_write[31:24] <= registers[rs2][7:0]; 420 | 421 | mem_write_mask[0] <= ~(ea[1:0] == 0); 422 | mem_write_mask[1] <= ~(ea[1:0] == 1); 423 | mem_write_mask[2] <= ~(ea[1:0] == 2); 424 | mem_write_mask[3] <= ~(ea[1:0] == 3); 425 | end 426 | 3'b001: 427 | begin 428 | mem_write[15:0] <= registers[rs2][15:0]; 429 | mem_write[31:16] <= registers[rs2][15:0]; 430 | 431 | mem_write_mask[0] <= ea[1:0] == 2; 432 | mem_write_mask[1] <= ea[1:0] == 2; 433 | mem_write_mask[2] <= ea[1:0] == 0; 434 | mem_write_mask[3] <= ea[1:0] == 0; 435 | end 436 | 3'b010: 437 | begin 438 | mem_write <= registers[rs2]; 439 | mem_write_mask <= 4'b0000; 440 | end 441 | endcase 442 | 443 | mem_write_enable <= 1; 444 | mem_bus_enable <= 1; 445 | state <= STATE_STORE_1; 446 | end 447 | STATE_STORE_1: 448 | begin 449 | mem_bus_enable <= 0; 450 | mem_write_enable <= 0; 451 | state <= STATE_FETCH_OP_0; 452 | end 453 | STATE_ALU_0: 454 | begin 455 | // ALU reg, reg. 456 | case (funct3) 457 | 3'b000: 458 | case (funct7) 459 | 7'h00: result <= registers[rs1] + source; 460 | // Doesn't fit on iCE40 HX8K. 461 | //7'h01: result <= $signed(registers[rs1]) * $signed(source); 462 | 7'h20: result <= registers[rs1] - source; 463 | endcase 464 | 3'b001: result <= registers[rs1] << source; 465 | 3'b010: result <= $signed(registers[rs1]) < $signed(source) ? 1 : 0; 466 | 3'b011: result <= registers[rs1] < source; 467 | 3'b100: result <= registers[rs1] ^ source; 468 | 3'b101: 469 | if (funct7 == 0) 470 | result <= registers[rs1] >> source; 471 | else 472 | result <= $signed(registers[rs1]) >>> source; 473 | 3'b110: result <= registers[rs1] | source; 474 | 3'b111: result <= registers[rs1] & source; 475 | endcase 476 | 477 | state <= STATE_ALU_1; 478 | end 479 | STATE_ALU_1: 480 | begin 481 | registers[rd] <= result; 482 | state <= STATE_FETCH_OP_0; 483 | end 484 | STATE_BRANCH_1: 485 | begin 486 | case (funct3) 487 | 3'b000: 488 | if (registers[rs1] == source) 489 | pc <= branch_address; 490 | 3'b001: 491 | if (registers[rs1] != source) 492 | pc <= branch_address; 493 | 3'b100: 494 | if ($signed(registers[rs1]) < $signed(source)) 495 | pc <= branch_address; 496 | 3'b101: 497 | if ($signed(registers[rs1]) >= $signed(source)) 498 | pc <= branch_address; 499 | 3'b110: 500 | if (registers[rs1] < source) 501 | pc <= branch_address; 502 | 3'b111: 503 | if (registers[rs1] >= source) 504 | pc <= branch_address; 505 | endcase 506 | 507 | state <= STATE_FETCH_OP_0; 508 | end 509 | STATE_MANDELBROT_1: 510 | begin 511 | mandelbrot_start <= 1; 512 | mandelbrot_i <= registers[rs2]; 513 | state <= STATE_MANDELBROT_2; 514 | end 515 | STATE_MANDELBROT_2: 516 | begin 517 | if (!mandelbrot_busy) begin 518 | state <= STATE_FETCH_OP_0; 519 | registers[rd] <= mandelbrot_result; 520 | end 521 | mandelbrot_start <= 0; 522 | end 523 | STATE_HALTED: 524 | begin 525 | state <= STATE_HALTED; 526 | end 527 | STATE_ERROR: 528 | begin 529 | state <= STATE_ERROR; 530 | end 531 | STATE_DEBUG: 532 | begin 533 | state <= STATE_DEBUG; 534 | end 535 | STATE_EEPROM_START: 536 | begin 537 | // Initialize values for reading from SPI-like EEPROM. 538 | if (eeprom_ready) begin 539 | //eeprom_mem_address <= pc; 540 | eeprom_mem_address <= 16'hc000; 541 | eeprom_count <= 0; 542 | state <= STATE_EEPROM_READ; 543 | end 544 | end 545 | STATE_EEPROM_READ: 546 | begin 547 | // Set the next EEPROM address to read from and strobe. 548 | mem_bus_enable <= 0; 549 | eeprom_address <= eeprom_count; 550 | eeprom_strobe <= 1; 551 | state <= STATE_EEPROM_WAIT; 552 | end 553 | STATE_EEPROM_WAIT: 554 | begin 555 | // Wait until 8 bits are clocked in. 556 | eeprom_strobe <= 0; 557 | 558 | if (eeprom_ready) begin 559 | 560 | if (eeprom_count[1:0] == 3) begin 561 | mem_address <= eeprom_mem_address; 562 | mem_write_mask <= 4'b0000; 563 | // After reading 4 bytes, store the 32 bit value to RAM. 564 | mem_write <= { 565 | eeprom_data_out, 566 | eeprom_holding[2], 567 | eeprom_holding[1], 568 | eeprom_holding[0] 569 | }; 570 | 571 | state <= STATE_EEPROM_WRITE; 572 | end else begin 573 | // Read 3 bytes into a holding register. 574 | eeprom_holding[eeprom_count[1:0]] <= eeprom_data_out; 575 | state <= STATE_EEPROM_READ; 576 | end 577 | 578 | eeprom_count <= eeprom_count + 1; 579 | end 580 | end 581 | STATE_EEPROM_WRITE: 582 | begin 583 | // Write value read from EEPROM into memory. 584 | mem_bus_enable <= 1; 585 | mem_write_enable <= 1; 586 | eeprom_mem_address <= eeprom_mem_address + 4; 587 | 588 | state <= STATE_EEPROM_DONE; 589 | end 590 | STATE_EEPROM_DONE: 591 | begin 592 | // Finish writing and read next byte if needed. 593 | mem_bus_enable <= 0; 594 | mem_write_enable <= 0; 595 | 596 | if (eeprom_count == 0) begin 597 | // Read in 2048 bytes. 598 | state <= STATE_FETCH_OP_0; 599 | end else 600 | state <= STATE_EEPROM_READ; 601 | end 602 | endcase 603 | end 604 | 605 | memory_bus memory_bus_0( 606 | .address (mem_address), 607 | .data_in (mem_write), 608 | .write_mask (mem_write_mask), 609 | .data_out (mem_read), 610 | //.debug (mem_debug), 611 | //.data_ready (mem_data_ready), 612 | .bus_enable (mem_bus_enable), 613 | .write_enable (mem_write_enable), 614 | .clk (clk), 615 | .raw_clk (raw_clk), 616 | .speaker_p (speaker_p), 617 | .speaker_m (speaker_m), 618 | .ioport_0 (ioport_0), 619 | .ioport_1 (ioport_1), 620 | .ioport_2 (ioport_2), 621 | .ioport_3 (ioport_3), 622 | .button_0 (button_0), 623 | .reset (~button_reset), 624 | .spi_clk (spi_clk), 625 | .spi_mosi (spi_mosi), 626 | .spi_miso (spi_miso) 627 | ); 628 | 629 | eeprom eeprom_0 630 | ( 631 | .address (eeprom_address), 632 | .strobe (eeprom_strobe), 633 | .raw_clk (raw_clk), 634 | .eeprom_cs (eeprom_cs), 635 | .eeprom_clk (eeprom_clk), 636 | .eeprom_di (eeprom_di), 637 | .eeprom_do (eeprom_do), 638 | .ready (eeprom_ready), 639 | .data_out (eeprom_data_out) 640 | ); 641 | 642 | mandelbrot mandelbrot_0 643 | ( 644 | .raw_clk (raw_clk), 645 | .start (mandelbrot_start), 646 | .curr_r (mandelbrot_r), 647 | .curr_i (mandelbrot_i), 648 | .result (mandelbrot_result), 649 | .busy (mandelbrot_busy) 650 | ); 651 | 652 | endmodule 653 | 654 | -------------------------------------------------------------------------------- /src/ciscv.v: -------------------------------------------------------------------------------- 1 | // RISC-V FPGA Soft Processor 2 | // Author: Michael Kohn 3 | // Email: mike@mikekohn.net 4 | // Web: https://www.mikekohn.net/ 5 | // Board: iceFUN iCE40 HX8K 6 | // License: MIT 7 | // 8 | // Copyright 2023-2024 by Michael Kohn 9 | // 10 | // CISC-V adds: 11 | // rd op 12 | // li.w 0000 000 00000 00000 010 rrrrr 1110111 (load 32 bit value) 13 | // li.d 0000 000 00000 00000 011 rrrrr 1110111 (load 64 bit value) 14 | // 15 | // rs2 rs1 rd 16 | // lb.base 0100 000 00000 00000 000 rrrrr 0110111 (lb.base rd, rs1, rs2) 17 | // lh.base 0100 000 00000 00000 001 rrrrr 0110111 rd <- [rs1 + rs2] 18 | // lw.base 0100 000 00000 00000 010 rrrrr 0110111 19 | // lbu.base 0100 000 00000 00000 100 rrrrr 0110111 20 | // lhu.base 0100 000 00000 00000 101 rrrrr 0110111 21 | // sb.base 1000 000 00000 00000 000 rrrrr 0110111 (sb.base rd, rs1, rs2) 22 | // sh.base 1000 000 00000 00000 001 rrrrr 0110111 rd -> [rs1 + rs2] 23 | // sw.base 1000 000 00000 00000 010 rrrrr 0110111 24 | 25 | // rs2 rs1 rd 26 | // lb.pre iiii iiii iiii rrrrr 000 rrrrr 0001011 (update rs1, rd <- [rs1]) 27 | // lh.pre iiii iiii iiii rrrrr 001 rrrrr 0001011 28 | // lw.pre iiii iiii iiii rrrrr 010 rrrrr 0001011 29 | // lbu.pre iiii iiii iiii rrrrr 100 rrrrr 0001011 30 | // lhu.pre iiii iiii iiii rrrrr 101 rrrrr 0001011 31 | // sb.pre iiiiiii rrrrr rrrrr 000 iiiii 0101011 (update rs2, rd -> [rs2]) 32 | // sh.pre iiiiiii rrrrr rrrrr 001 iiiii 0101011 33 | // sw.pre iiiiiii rrrrr rrrrr 010 iiiii 0101011 34 | 35 | // lb.post iiii iiii iiii rrrrr 000 rrrrr 0001111 (rd <- [rs1], update rs1) 36 | // lh.post iiii iiii iiii rrrrr 000 rrrrr 0001111 37 | // lw.post iiii iiii iiii rrrrr 010 rrrrr 0001111 38 | // lbu.pre iiii iiii iiii rrrrr 010 rrrrr 0001111 39 | // lbu.post iiii iiii iiii rrrrr 000 rrrrr 0001111 40 | // lhu.post iiii iiii iiii rrrrr 000 rrrrr 0001111 41 | // sb.post iiiiiii rrrrr rrrrr 000 iiiii 0101111 (rd -> [rs2], update rs2) 42 | // sh.post iiiiiii rrrrr rrrrr 001 iiiii 0101111 43 | // sw.post iiiiiii rrrrr rrrrr 010 iiiii 0101111 44 | 45 | module riscv 46 | ( 47 | output [7:0] leds, 48 | output [3:0] column, 49 | input raw_clk, 50 | output eeprom_cs, 51 | output eeprom_clk, 52 | output eeprom_di, 53 | input eeprom_do, 54 | output speaker_p, 55 | output speaker_m, 56 | output ioport_0, 57 | output ioport_1, 58 | output ioport_2, 59 | output ioport_3, 60 | input button_reset, 61 | input button_halt, 62 | input button_program_select, 63 | input button_0, 64 | output spi_clk, 65 | output spi_mosi, 66 | input spi_miso 67 | ); 68 | 69 | // iceFUN 8x4 LEDs used for debugging. 70 | reg [7:0] leds_value; 71 | reg [3:0] column_value; 72 | 73 | assign leds = leds_value; 74 | assign column = column_value; 75 | 76 | // Memory bus (ROM, RAM, peripherals). 77 | reg [15:0] mem_address = 0; 78 | reg [31:0] mem_write = 0; 79 | reg [3:0] mem_write_mask = 0; 80 | wire [31:0] mem_read; 81 | //wire mem_data_ready; 82 | reg mem_bus_enable = 0; 83 | reg mem_write_enable = 0; 84 | 85 | //wire [7:0] mem_debug; 86 | 87 | // Clock. 88 | reg [21:0] count = 0; 89 | reg [4:0] state = 0; 90 | reg [19:0] clock_div; 91 | reg [14:0] delay_loop; 92 | wire clk; 93 | assign clk = clock_div[1]; 94 | 95 | // Registers. 96 | //wire [31:0] registers [0]; 97 | //assign registers[0] = 0; 98 | reg [31:0] registers [31:0]; 99 | reg [15:0] pc = 0; 100 | reg [15:0] pc_current = 0; 101 | 102 | // Instruction 103 | reg [31:0] instruction; 104 | wire [6:0] op; 105 | wire [4:0] rd; 106 | wire [4:0] rs1; 107 | wire [4:0] rs2; 108 | wire [4:0] shamt; 109 | wire [2:0] funct3; 110 | wire [6:0] funct7; 111 | wire [12:0] branch_offset; 112 | wire [2:0] memory_size; 113 | assign op = instruction[6:0]; 114 | assign rd = instruction[11:7]; 115 | assign rs1 = instruction[19:15]; 116 | assign rs2 = instruction[24:20]; 117 | assign shamt = instruction[24:20]; 118 | assign funct3 = instruction[14:12]; 119 | assign funct7 = instruction[31:25]; 120 | assign branch_offset = { 121 | instruction[31], 122 | instruction[7], 123 | instruction[30:25], 124 | instruction[11:8], 125 | 1'b0 126 | }; 127 | 128 | reg [31:0] source; 129 | reg [31:0] temp; 130 | //wire writeback; 131 | //assign writeback = ~instruction[2]; 132 | //reg writeback; 133 | 134 | // Load / Store. 135 | assign memory_size = instruction[14:12]; 136 | reg [31:0] ea; 137 | reg [31:0] ea_aligned; 138 | 139 | // Lower 6 its of the instruction. 140 | wire [5:0] opcode; 141 | assign opcode = instruction[5:0]; 142 | 143 | // Eeprom. 144 | reg [10:0] eeprom_count; 145 | wire [7:0] eeprom_data_out; 146 | reg [7:0] eeprom_holding [3:0]; 147 | reg [10:0] eeprom_address; 148 | reg [15:0] eeprom_mem_address; 149 | reg eeprom_strobe = 0; 150 | wire eeprom_ready; 151 | 152 | // Mandelbrot. 153 | reg [15:0] mandelbrot_r; 154 | reg [15:0] mandelbrot_i; 155 | wire mandelbrot_busy; 156 | reg mandelbrot_start = 0; 157 | wire [3:0] mandelbrot_result; 158 | 159 | // Debug. 160 | //reg [7:0] debug_0 = 0; 161 | //reg [7:0] debug_1 = 0; 162 | //reg [7:0] debug_2 = 0; 163 | //reg [7:0] debug_3 = 0; 164 | 165 | // This block is simply a clock divider for the raw_clk. 166 | always @(posedge raw_clk) begin 167 | count <= count + 1; 168 | clock_div <= clock_div + 1; 169 | end 170 | 171 | // Debug: This block simply drives the 8x4 LEDs. 172 | always @(posedge raw_clk) begin 173 | case (count[9:7]) 174 | 3'b000: begin column_value <= 4'b0111; leds_value <= ~registers[6][7:0]; end 175 | 3'b010: begin column_value <= 4'b1011; leds_value <= ~registers[6][15:8]; end 176 | //3'b010: begin column_value <= 4'b1011; leds_value <= ~instruction[7:0]; end 177 | 3'b100: begin column_value <= 4'b1101; leds_value <= ~pc[7:0]; end 178 | 3'b110: begin column_value <= 4'b1110; leds_value <= ~state; end 179 | default: begin column_value <= 4'b1111; leds_value <= 8'hff; end 180 | endcase 181 | end 182 | 183 | parameter STATE_RESET = 0; 184 | parameter STATE_DELAY_LOOP = 1; 185 | parameter STATE_FETCH_OP_0 = 2; 186 | parameter STATE_FETCH_OP_1 = 3; 187 | parameter STATE_START_DECODE = 4; 188 | parameter STATE_EXECUTE_E = 5; 189 | parameter STATE_FETCH_LOAD_1 = 6; 190 | 191 | parameter STATE_STORE_0 = 7; 192 | parameter STATE_STORE_1 = 8; 193 | 194 | parameter STATE_ALU_0 = 9; 195 | //parameter STATE_ALU_1 = 10; 196 | 197 | parameter STATE_BRANCH_1 = 11; 198 | parameter STATE_MANDELBROT_1 = 12; 199 | parameter STATE_MANDELBROT_2 = 13; 200 | 201 | parameter STATE_HALTED = 19; 202 | parameter STATE_ERROR = 20; 203 | parameter STATE_DEBUG = 21; 204 | parameter STATE_EEPROM_START = 22; 205 | parameter STATE_EEPROM_READ = 23; 206 | parameter STATE_EEPROM_WAIT = 24; 207 | parameter STATE_EEPROM_WRITE = 25; 208 | parameter STATE_EEPROM_DONE = 26; 209 | 210 | function signed [31:0] sign12(input signed [11:0] data); 211 | sign12 = data; 212 | endfunction 213 | 214 | //`define sign_imm12(data) { {20{ data[31] }}, data[31:20] } 215 | 216 | // This block is the main CPU instruction execute state machine. 217 | always @(posedge clk) begin 218 | if (!button_reset) 219 | state <= STATE_RESET; 220 | else if (!button_halt) 221 | state <= STATE_HALTED; 222 | else 223 | case (state) 224 | STATE_RESET: 225 | begin 226 | mem_address <= 0; 227 | mem_write_enable <= 0; 228 | mem_write <= 0; 229 | instruction <= 0; 230 | delay_loop <= 12000; 231 | //eeprom_strobe <= 0; 232 | mandelbrot_start <= 0; 233 | state <= STATE_DELAY_LOOP; 234 | end 235 | STATE_DELAY_LOOP: 236 | begin 237 | // This is probably not needed. The chip starts up fine without it. 238 | if (delay_loop == 0) begin 239 | 240 | // If button is not pushed, start rom.v code otherwise use EEPROM. 241 | if (button_program_select) begin 242 | pc <= 16'h4000; 243 | state <= STATE_FETCH_OP_0; 244 | end else begin 245 | pc <= 16'hc000; 246 | state <= STATE_EEPROM_START; 247 | end 248 | end else begin 249 | delay_loop <= delay_loop - 1; 250 | end 251 | end 252 | STATE_FETCH_OP_0: 253 | begin 254 | registers[0] <= 0; 255 | mem_bus_enable <= 1; 256 | mem_write_enable <= 0; 257 | mem_address <= pc; 258 | //writeback <= 0; 259 | pc_current = pc; 260 | pc <= pc + 4; 261 | state <= STATE_FETCH_OP_1; 262 | end 263 | STATE_FETCH_OP_1: 264 | begin 265 | mem_bus_enable <= 0; 266 | instruction <= mem_read; 267 | state <= STATE_START_DECODE; 268 | end 269 | STATE_START_DECODE: 270 | begin 271 | case (op) 272 | 7'b0110111: 273 | begin 274 | // lui. 275 | registers[rd] <= { instruction[31:12], 12'h000 }; 276 | state <= STATE_FETCH_OP_0; 277 | end 278 | 7'b0010111: 279 | begin 280 | // auipc. 281 | registers[rd] <= pc_current + { instruction[31:12], 12'b0 }; 282 | state <= STATE_FETCH_OP_0; 283 | end 284 | 7'b1110111: 285 | begin 286 | case (instruction[31:30]) 287 | 2'b01: 288 | begin 289 | // CISC-V: lx.base rd, rs1, rs2 290 | ea <= registers[rs1] + registers[rs2]; 291 | mem_bus_enable <= 1; 292 | mem_write_enable <= 0; 293 | mem_address <= registers[rs1] + registers[rs2]; 294 | state <= STATE_FETCH_LOAD_1; 295 | end 296 | 2'b10: 297 | begin 298 | // CISC-V: sx.base rd, rs1, rs2 299 | ea <= registers[rs1] + registers[rs2]; 300 | mem_address <= registers[rs1] + registers[rs2]; 301 | mem_bus_enable <= 0; 302 | state <= STATE_STORE_0; 303 | end 304 | default: 305 | begin 306 | // CISC-V: li.w rd, imm 307 | ea <= pc; 308 | pc <= pc + 4; 309 | mem_bus_enable <= 1; 310 | mem_write_enable <= 0; 311 | mem_address <= pc; 312 | state <= STATE_FETCH_LOAD_1; 313 | end 314 | endcase 315 | end 316 | 7'b0111011: 317 | begin 318 | // mandel rd, rs1, rs2 (aka feq.d) 319 | if (funct7 == 7'b0000001 && funct3 == 3'b000) begin 320 | mandelbrot_r <= registers[rs1]; 321 | state <= STATE_MANDELBROT_1; 322 | end 323 | end 324 | 7'b1101111: 325 | begin 326 | // jal. 327 | registers[rd] <= pc; 328 | 329 | pc <= $signed(pc_current) + $signed( { 330 | instruction[31], 331 | instruction[19:12], 332 | instruction[20], 333 | instruction[30:21], 334 | 1'b0 335 | } ); 336 | 337 | state <= STATE_FETCH_OP_0; 338 | end 339 | 7'b1100111: 340 | begin 341 | // jalr. 342 | pc <= ($signed(registers[rs1]) + $signed(instruction[31:20])) & 16'hfffc; 343 | //temp <= pc; 344 | //state <= STATE_ALU_1; 345 | registers[rd] <= pc; 346 | state <= STATE_FETCH_OP_0; 347 | end 348 | 7'b1100011: 349 | begin 350 | // branch. 351 | source <= registers[rs2]; 352 | state <= STATE_BRANCH_1; 353 | end 354 | 7'b0000011: 355 | begin 356 | // Load. 357 | ea <= registers[rs1] + sign12(instruction[31:20]); 358 | mem_bus_enable <= 1; 359 | mem_write_enable <= 0; 360 | mem_address <= registers[rs1] + sign12(instruction[31:20]); 361 | state <= STATE_FETCH_LOAD_1; 362 | end 363 | 7'b0001011: 364 | begin 365 | // Load.pre CISC-V. 366 | ea <= registers[rs1] + sign12(instruction[31:20]); 367 | registers[rs1] <= registers[rs1] + sign12(instruction[31:20]); 368 | mem_bus_enable <= 1; 369 | mem_write_enable <= 0; 370 | mem_address <= registers[rs1] + sign12(instruction[31:20]); 371 | state <= STATE_FETCH_LOAD_1; 372 | end 373 | 7'b0001111: 374 | begin 375 | // Load.post CISC-V. 376 | registers[rs1] <= registers[rs1] + sign12(instruction[31:20]); 377 | ea <= registers[rs1]; 378 | mem_bus_enable <= 1; 379 | mem_write_enable <= 0; 380 | mem_address <= registers[rs1]; 381 | state <= STATE_FETCH_LOAD_1; 382 | end 383 | 7'b0100011: 384 | begin 385 | // Store. 386 | ea <= registers[rs1] + sign12( { funct7, instruction[11:7] } ); 387 | mem_address <= registers[rs1] + sign12( { funct7, instruction[11:7] } ); 388 | mem_bus_enable <= 0; 389 | state <= STATE_STORE_0; 390 | end 391 | 7'b0101011: 392 | begin 393 | // Store.pre CISC-V. 394 | ea <= registers[rs1] + sign12( { funct7, instruction[11:7] } ); 395 | registers[rs1] <= registers[rs1] + sign12( { funct7, instruction[11:7] } ); 396 | mem_address <= registers[rs1] + sign12( { funct7, instruction[11:7] } ); 397 | mem_bus_enable <= 0; 398 | state <= STATE_STORE_0; 399 | end 400 | 7'b0101111: 401 | begin 402 | // Store.post CISC-V. 403 | registers[rs1] <= registers[rs1] + sign12( { funct7, instruction[11:7] } ); 404 | ea <= registers[rs1]; 405 | mem_address <= registers[rs1]; 406 | mem_bus_enable <= 0; 407 | state <= STATE_STORE_0; 408 | end 409 | 7'b0010011: 410 | begin 411 | // ALU immediate. 412 | case (funct3) 413 | 3'b000: registers[rd] <= $signed(registers[rs1]) + $signed(instruction[31:20]); 414 | 3'b010: registers[rd] <= $signed(registers[rs1]) < sign12(instruction[31:20]); 415 | 3'b011: registers[rd] <= $signed(registers[rs1]) < instruction[31:20]; 416 | 3'b100: registers[rd] <= registers[rs1] ^ sign12(instruction[31:20]); 417 | 3'b110: registers[rd] <= registers[rs1] | sign12(instruction[31:20]); 418 | 3'b111: registers[rd] <= registers[rs1] & sign12(instruction[31:20]); 419 | // Shift. 420 | 3'b001: registers[rd] <= registers[rs1] << shamt; 421 | 3'b101: 422 | if (funct7 == 0) 423 | registers[rd] <= registers[rs1] >> shamt; 424 | else 425 | registers[rd] <= $signed(registers[rs1]) >>> shamt; 426 | endcase 427 | 428 | //state <= STATE_ALU_1; 429 | state <= STATE_FETCH_OP_0; 430 | end 431 | 7'b0110011: 432 | begin 433 | // ALU reg, reg. 434 | source <= registers[rs2]; 435 | state <= STATE_ALU_0; 436 | end 437 | 7'b1110011: 438 | begin 439 | state <= STATE_EXECUTE_E; 440 | end 441 | default 442 | begin 443 | state <= STATE_ERROR; 444 | end 445 | endcase 446 | end 447 | STATE_EXECUTE_E: 448 | begin 449 | // Since this core only supports "ebreak", send all instructions 450 | // to the halted state. 451 | state <= STATE_HALTED; 452 | end 453 | STATE_FETCH_LOAD_1: 454 | begin 455 | mem_bus_enable <= 0; 456 | 457 | case (memory_size[1:0]) 458 | 3'b00: 459 | begin 460 | case (ea[1:0]) 461 | 0: 462 | begin 463 | registers[rd][7:0] <= mem_read[7:0]; 464 | registers[rd][31:8] <= { {24{ mem_read[7] & ~memory_size[2] } } }; 465 | end 466 | 1: 467 | begin 468 | registers[rd][7:0] <= mem_read[15:8]; 469 | registers[rd][31:8] <= { {24{ mem_read[15] & ~memory_size[2] } } }; 470 | end 471 | 2: 472 | begin 473 | registers[rd][7:0] <= mem_read[23:16]; 474 | registers[rd][31:8] <= { {24{ mem_read[23] & ~memory_size[2] } } }; 475 | end 476 | 3: 477 | begin 478 | registers[rd][7:0] <= mem_read[31:24]; 479 | registers[rd][31:8] <= { {24{ mem_read[31] & ~memory_size[2] } } }; 480 | end 481 | endcase 482 | end 483 | 3'b01: 484 | begin 485 | case (ea[1]) 486 | 0: 487 | begin 488 | registers[rd][15:0] <= mem_read[15:0]; 489 | registers[rd][31:16] <= { {16{ mem_read[15] & ~memory_size[2] } } }; 490 | end 491 | 1: 492 | begin 493 | registers[rd][15:0] <= mem_read[31:16]; 494 | registers[rd][31:16] <= { {16{ mem_read[31] & ~memory_size[2] } } }; 495 | end 496 | endcase 497 | end 498 | 3'b10: 499 | begin 500 | registers[rd] <= mem_read; 501 | end 502 | endcase 503 | 504 | //if (writeback) registers[rs1] <= ea; 505 | state <= STATE_FETCH_OP_0; 506 | end 507 | STATE_STORE_0: 508 | begin 509 | case (funct3) 510 | 3'b000: 511 | begin 512 | case (ea[1:0]) 513 | 2'b00: 514 | begin 515 | mem_write <= { 24'h0000, registers[rs2][7:0] }; 516 | mem_write_mask <= 4'b1110; 517 | end 518 | 2'b01: 519 | begin 520 | mem_write <= { 16'h0000, registers[rs2][7:0], 8'h00 }; 521 | mem_write_mask <= 4'b1101; 522 | end 523 | 2'b10: 524 | begin 525 | mem_write <= { 8'h00, registers[rs2][7:0], 16'h0000 }; 526 | mem_write_mask <= 4'b1011; 527 | end 528 | 2'b11: 529 | begin 530 | mem_write <= { registers[rs2][7:0], 24'h0000 }; 531 | mem_write_mask <= 4'b0111; 532 | end 533 | endcase 534 | end 535 | 3'b001: 536 | begin 537 | case (ea[1:0]) 538 | 2'b00: 539 | begin 540 | mem_write <= { 16'h0000, registers[rs2][15:0] }; 541 | mem_write_mask <= 4'b1100; 542 | end 543 | 2'b10: 544 | begin 545 | mem_write <= { registers[rs2][15:0], 16'h0000 }; 546 | mem_write_mask <= 4'b0011; 547 | end 548 | endcase 549 | end 550 | 3'b010: 551 | begin 552 | mem_write <= registers[rs2]; 553 | mem_write_mask <= 4'b0000; 554 | end 555 | endcase 556 | 557 | mem_write_enable <= 1; 558 | mem_bus_enable <= 1; 559 | state <= STATE_STORE_1; 560 | end 561 | STATE_STORE_1: 562 | begin 563 | mem_bus_enable <= 0; 564 | mem_write_enable <= 0; 565 | //if (writeback) registers[rs2] <= ea; 566 | state <= STATE_FETCH_OP_0; 567 | end 568 | STATE_ALU_0: 569 | begin 570 | // ALU reg, reg. 571 | case (funct3) 572 | 3'b000: 573 | case (funct7) 574 | 7'h00: registers[rd] <= registers[rs1] + source; 575 | // Doesn't fit on iCE40 HX8K. 576 | //7'h01: registers[rd] <= $signed(registers[rs1]) * $signed(source); 577 | 7'h20: registers[rd] <= registers[rs1] - source; 578 | endcase 579 | 3'b001: registers[rd] <= registers[rs1] << source; 580 | 3'b010: registers[rd] <= $signed(registers[rs1]) < $signed(source) ? 1 : 0; 581 | 3'b011: registers[rd] <= registers[rs1] < source; 582 | 3'b100: registers[rd] <= registers[rs1] ^ source; 583 | 3'b101: 584 | if (funct7 == 0) 585 | registers[rd] <= registers[rs1] >> source; 586 | else 587 | registers[rd] <= $signed(registers[rs1]) >>> source; 588 | 3'b110: registers[rd] <= registers[rs1] | source; 589 | 3'b111: registers[rd] <= registers[rs1] & source; 590 | endcase 591 | 592 | //state <= STATE_ALU_1; 593 | state <= STATE_FETCH_OP_0; 594 | end 595 | /* 596 | STATE_ALU_1: 597 | begin 598 | registers[rd] <= temp; 599 | state <= STATE_FETCH_OP_0; 600 | end 601 | */ 602 | STATE_BRANCH_1: 603 | begin 604 | case (funct3) 605 | 3'b000: 606 | if (registers[rs1] == source) 607 | pc <= $signed(pc_current) + $signed(branch_offset); 608 | 3'b001: 609 | if (registers[rs1] != source) 610 | pc <= $signed(pc_current) + $signed(branch_offset); 611 | 3'b100: 612 | if ($signed(registers[rs1]) < $signed(source)) 613 | pc <= $signed(pc_current) + $signed(branch_offset); 614 | 3'b101: 615 | if ($signed(registers[rs1]) >= $signed(source)) 616 | pc <= $signed(pc_current) + $signed(branch_offset); 617 | 3'b110: 618 | if (registers[rs1] < source) 619 | pc <= $signed(pc_current) + $signed(branch_offset); 620 | 3'b111: 621 | if (registers[rs1] >= source) 622 | pc <= $signed(pc_current) + $signed(branch_offset); 623 | endcase 624 | 625 | state <= STATE_FETCH_OP_0; 626 | end 627 | STATE_MANDELBROT_1: 628 | begin 629 | mandelbrot_start <= 1; 630 | mandelbrot_i <= registers[rs2]; 631 | state <= STATE_MANDELBROT_2; 632 | end 633 | STATE_MANDELBROT_2: 634 | begin 635 | if (!mandelbrot_busy) begin 636 | state <= STATE_FETCH_OP_0; 637 | registers[rd] <= mandelbrot_result; 638 | end 639 | mandelbrot_start <= 0; 640 | end 641 | STATE_HALTED: 642 | begin 643 | state <= STATE_HALTED; 644 | end 645 | STATE_ERROR: 646 | begin 647 | state <= STATE_ERROR; 648 | end 649 | STATE_DEBUG: 650 | begin 651 | state <= STATE_DEBUG; 652 | end 653 | STATE_EEPROM_START: 654 | begin 655 | // Initialize values for reading from SPI-like EEPROM. 656 | if (eeprom_ready) begin 657 | //eeprom_mem_address <= pc; 658 | eeprom_mem_address <= 16'hc000; 659 | eeprom_count <= 0; 660 | state <= STATE_EEPROM_READ; 661 | end 662 | end 663 | STATE_EEPROM_READ: 664 | begin 665 | // Set the next EEPROM address to read from and strobe. 666 | mem_bus_enable <= 0; 667 | eeprom_address <= eeprom_count; 668 | eeprom_strobe <= 1; 669 | state <= STATE_EEPROM_WAIT; 670 | end 671 | STATE_EEPROM_WAIT: 672 | begin 673 | // Wait until 8 bits are clocked in. 674 | eeprom_strobe <= 0; 675 | 676 | if (eeprom_ready) begin 677 | 678 | if (eeprom_count[1:0] == 3) begin 679 | mem_address <= eeprom_mem_address; 680 | mem_write_mask <= 4'b0000; 681 | // After reading 4 bytes, store the 32 bit value to RAM. 682 | mem_write <= { 683 | eeprom_data_out, 684 | eeprom_holding[2], 685 | eeprom_holding[1], 686 | eeprom_holding[0] 687 | }; 688 | 689 | state <= STATE_EEPROM_WRITE; 690 | end else begin 691 | // Read 3 bytes into a holding register. 692 | eeprom_holding[eeprom_count[1:0]] <= eeprom_data_out; 693 | state <= STATE_EEPROM_READ; 694 | end 695 | 696 | eeprom_count <= eeprom_count + 1; 697 | end 698 | end 699 | STATE_EEPROM_WRITE: 700 | begin 701 | // Write value read from EEPROM into memory. 702 | mem_bus_enable <= 1; 703 | mem_write_enable <= 1; 704 | eeprom_mem_address <= eeprom_mem_address + 4; 705 | 706 | state <= STATE_EEPROM_DONE; 707 | end 708 | STATE_EEPROM_DONE: 709 | begin 710 | // Finish writing and read next byte if needed. 711 | mem_bus_enable <= 0; 712 | mem_write_enable <= 0; 713 | 714 | if (eeprom_count == 0) begin 715 | // Read in 2048 bytes. 716 | state <= STATE_FETCH_OP_0; 717 | end else 718 | state <= STATE_EEPROM_READ; 719 | end 720 | endcase 721 | end 722 | 723 | memory_bus memory_bus_0( 724 | .address (mem_address), 725 | .data_in (mem_write), 726 | .write_mask (mem_write_mask), 727 | .data_out (mem_read), 728 | //.debug (mem_debug), 729 | //.data_ready (mem_data_ready), 730 | .bus_enable (mem_bus_enable), 731 | .write_enable (mem_write_enable), 732 | .clk (clk), 733 | .raw_clk (raw_clk), 734 | .speaker_p (speaker_p), 735 | .speaker_m (speaker_m), 736 | .ioport_0 (ioport_0), 737 | .ioport_1 (ioport_1), 738 | .ioport_2 (ioport_2), 739 | .ioport_3 (ioport_3), 740 | .button_0 (button_0), 741 | .reset (~button_reset), 742 | .spi_clk (spi_clk), 743 | .spi_mosi (spi_mosi), 744 | .spi_miso (spi_miso) 745 | ); 746 | 747 | eeprom eeprom_0 748 | ( 749 | .address (eeprom_address), 750 | .strobe (eeprom_strobe), 751 | .raw_clk (raw_clk), 752 | .eeprom_cs (eeprom_cs), 753 | .eeprom_clk (eeprom_clk), 754 | .eeprom_di (eeprom_di), 755 | .eeprom_do (eeprom_do), 756 | .ready (eeprom_ready), 757 | .data_out (eeprom_data_out) 758 | ); 759 | 760 | mandelbrot mandelbrot_0 761 | ( 762 | .raw_clk (raw_clk), 763 | .start (mandelbrot_start), 764 | .curr_r (mandelbrot_r), 765 | .curr_i (mandelbrot_i), 766 | .result (mandelbrot_result), 767 | .busy (mandelbrot_busy) 768 | ); 769 | 770 | endmodule 771 | 772 | --------------------------------------------------------------------------------