diff --git a/apycula/chipdb.py b/apycula/chipdb.py index 570dea12..d6ee5e13 100644 --- a/apycula/chipdb.py +++ b/apycula/chipdb.py @@ -1832,6 +1832,122 @@ def fse_create_bandgap(dev, device): dev.extra_func.setdefault((10, 18), {}).update( {'bandgap': {'wire': 'C1'}}) +def fse_create_userflash(dev, device, dat): + # dat[‘UfbIns’] and dat[‘UfbOuts’]. + # The outputs are exactly 32 by the number of bits and they are always + # present, their positions correspond to bit indices - checked by + # selectively connecting the outputs to LEDs. + # The inputs depend on the Flash type - different types have different + # inputs, e.g. XY or RCP addressing is used etc. During experimental + # generation of images with input to button connection some inputs + # description could not be found in the table, such inputs will be + # specified here rigidly. + # Flash types (see UG295-1.4.3E_Gowin User Flash User Guide.pdf) + _flash_type = {'GW1N-1': 'FLASH96K', + 'GW1NZ-1': 'FLASH64KZ', + 'GW1N-4': 'FLASH256K', 'GW1NS-4': 'FLASH256K', + 'GW1N-9': 'FLASH608K', 'GW1N-9C': 'FLASH608K'} + if device not in _flash_type: + return + flash_type = _flash_type[device] + ins_type = 'XY' + if flash_type == 'FLASH96K': + ins_type = 'RC' + + # userflash has neither its own cell type nor fuses, so it is logical to make it extra func. + # use X0Y0 cell for convenience - a significant part of UserFlash pins are + # located there, it saves from creating unnecessary nodes + row, col = (0, 0) + dev.extra_func.setdefault((row, col), {}).update( + {'userflash': {'type': flash_type}}) + extra_func = dev.extra_func[(row, col)]['userflash'] + + + def make_port(r, c, wire, port, wire_type, pins): + if r == -1 or c == -1: + return + bel = Bel() + wire = wirenames[wire] + bel.portmap[port] = wire + if r - 1 != row or c - 1 != col : + create_port_wire(dev, row, col, r - row - 1, c - col - 1, bel, 'USERFLASH', port, wire, wire_type) + pins[port] = bel.portmap[port] + + # outputs + outs = extra_func.setdefault('outs', {}) + for i, desc in enumerate(dat.compat_dict['UfbOuts']): + port = f'DOUT{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_OUT', outs) + + # inputs + ins = extra_func.setdefault('ins', {}) + # DIN first - we know there they are + for i, desc in enumerate(dat.compat_dict['UfbIns'][58:]): + port = f'DIN{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + + if ins_type == 'RC': + for i, desc in enumerate(dat.compat_dict['UfbIns'][21:27]): + port = f'RA{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][27:33]): + port = f'CA{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][33:39]): + port = f'PA{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][39:43]): + port = f'MODE{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][43:45]): + port = f'SEQ{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][45:50]): + port = ['ACLK', 'PW', 'RESET', 'PE', 'OE'][i] + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][50:52]): + port = f'RMODE{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][52:54]): + port = f'WMODE{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][54:56]): + port = f'RBYTESEL{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][56:58]): + port = f'WBYTESEL{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + else: + for i, desc in enumerate(dat.compat_dict['UfbIns'][:6]): + port = ['XE', 'YE', 'SE', 'PROG', 'ERASE', 'NVSTR'][i] + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][6:15]): + port = f'XADR{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + for i, desc in enumerate(dat.compat_dict['UfbIns'][15:21]): + port = f'YADR{i}' + r, c, wire = desc + make_port(r, c, wire, port, 'FLASH_IN', ins) + + # XXX INUSEN - is observed to be connected to the VSS when USERFLASH is used + if flash_type != 'FLASH64KZ': + ins['INUSEN'] = 'C0' + + def fse_bram(fse, aux = False): bels = {} name = 'BSRAM' @@ -1970,6 +2086,7 @@ def from_fse(device, fse, dat: Datfile): fse_create_osc(dev, device, fse) fse_create_gsr(dev, device) fse_create_bandgap(dev, device) + fse_create_userflash(dev, device, dat) fse_create_logic2clk(dev, device, dat) disable_plls(dev, device) sync_extra_func(dev) diff --git a/apycula/gowin_pack.py b/apycula/gowin_pack.py index df1c0bdf..d81b73ed 100644 --- a/apycula/gowin_pack.py +++ b/apycula/gowin_pack.py @@ -185,7 +185,7 @@ def get_bits(init_data): def get_bels(data): later = [] if is_himbaechel: - belre = re.compile(r"X(\d+)Y(\d+)/(?:GSR|LUT|DFF|IOB|MUX|ALU|ODDR|OSC[ZFHWO]?|BUF[GS]|RAM16SDP4|RAM16SDP2|RAM16SDP1|PLL|IOLOGIC|CLKDIV2|CLKDIV|BSRAM|ALU|MULTALU18X18|MULTALU36X18|MULTADDALU18X18|MULT36X36|MULT18X18|MULT9X9|PADD18|PADD9|BANDGAP|DQCE|DCS)(\w*)") + belre = re.compile(r"X(\d+)Y(\d+)/(?:GSR|LUT|DFF|IOB|MUX|ALU|ODDR|OSC[ZFHWO]?|BUF[GS]|RAM16SDP4|RAM16SDP2|RAM16SDP1|PLL|IOLOGIC|CLKDIV2|CLKDIV|BSRAM|ALU|MULTALU18X18|MULTALU36X18|MULTADDALU18X18|MULT36X36|MULT18X18|MULT9X9|PADD18|PADD9|BANDGAP|DQCE|DCS|USERFLASH)(\w*)") else: belre = re.compile(r"R(\d+)C(\d+)_(?:GSR|SLICE|IOB|MUX2_LUT5|MUX2_LUT6|MUX2_LUT7|MUX2_LUT8|ODDR|OSC[ZFHWO]?|BUFS|RAMW|rPLL|PLLVR|IOLOGIC)(\w*)") @@ -1996,13 +1996,13 @@ def bin_str_to_dec(str_val): dec_num = int(bin_str[0], 2) return str(dec_num) return None - + _hclk_default_params ={"GSREN": "false", "DIV_MODE":"2"} def set_hclk_attrs(db, params, num, typ, cell_name): name_pattern = r'^_HCLK([0,1])_SECT([0,1])$' - params = dict(params or _hclk_default_params) + params = dict(params or _hclk_default_params) attrs = {} pattern_match = re.findall(name_pattern, num) if (not pattern_match): @@ -2013,21 +2013,21 @@ def set_hclk_attrs(db, params, num, typ, cell_name): if device in ["GW1N-1S","GW1N-2","GW1NR-2","GW1NS-4","GW1NS-4C","GW1NSR-4",\ "GW1NSR-4C","GW1NSER-4C","GW1N-9","GW1NR-9", "GW1N-9C","GW1NR-9C","GW1N-1P5"]: valid_div_modes.append("8") - + if (params["DIV_MODE"]) not in valid_div_modes: bin_match = bin_str_to_dec(params["DIV_MODE"]) if bin_match is None or bin_match not in valid_div_modes: raise Exception(f"Invalid DIV_MODE {bin_match or params['DIV_MODE']} for CLKDIV {cell_name} on device {device}") params["DIV_MODE"] = str(bin_match[0]) - + if (typ == "CLKDIV2"): attrs[f"BK{section_idx}MUX{hclk_idx}_OUTSEL"] = "DIV2" elif (typ == "CLKDIV"): - attrs[f"HCLKDIV{hclk_idx}_DIV"] = params["DIV_MODE"] + attrs[f"HCLKDIV{hclk_idx}_DIV"] = params["DIV_MODE"] if (section_idx == '1'): attrs[f"HCLKDCS{hclk_idx}_SEL"] = f"HCLKBK{section_idx}{hclk_idx}" - + fin_attrs = set() for attr, val in attrs.items(): if isinstance(val, str): @@ -2313,6 +2313,8 @@ def place(db, tilemap, bels, cst, args): pass elif typ == "BANDGAP": pass + elif typ.startswith("FLASH"): + pass elif typ.startswith('MUX2_'): pass elif typ == "BUFS": diff --git a/examples/himbaechel/Makefile.himbaechel b/examples/himbaechel/Makefile.himbaechel index 80925219..eef31be8 100644 --- a/examples/himbaechel/Makefile.himbaechel +++ b/examples/himbaechel/Makefile.himbaechel @@ -49,7 +49,7 @@ all: \ bsram-pROM-tangnano1k.fs bsram-SDPB-tangnano1k.fs bsram-DPB16-tangnano1k.fs \ bsram-SP-tangnano1k.fs bsram-pROMX9-tangnano1k.fs bsram-SDPX9B-tangnano1k.fs \ bsram-SPX9-tangnano1k.fs bsram-DPX9B18-tangnano1k.fs \ - dqce-tangnano1k.fs dcs-tangnano1k.fs \ + dqce-tangnano1k.fs dcs-tangnano1k.fs userflash-tangnano1k.fs \ \ blinky-tangnano4k.fs shift-tangnano4k.fs blinky-tbuf-tangnano4k.fs blinky-oddr-tangnano4k.fs \ blinky-osc-tangnano4k.fs tlvds-tangnano4k.fs elvds-tangnano4k.fs oddr-tlvds-tangnano4k.fs \ @@ -78,7 +78,7 @@ all: \ dsp-mult36x36-tangnano9k.fs dsp-padd9-tangnano9k.fs dsp-padd18-tangnano9k.fs \ dsp-mult9x9-tangnano9k.fs dsp-alu54d-tangnano9k.fs dsp-multalu18x18-tangnano9k.fs \ dsp-multalu36x18-tangnano9k.fs dsp-multaddalu18x18-tangnano9k.fs \ - dqce-tangnano9k.fs dcs-tangnano9k.fs \ + dqce-tangnano9k.fs dcs-tangnano9k.fs femto-riscv-userflash-tangnano9k.fs \ \ blinky-szfpga.fs shift-szfpga.fs blinky-tbuf-szfpga.fs blinky-oddr-szfpga.fs \ blinky-osc-szfpga.fs tlvds-szfpga.fs elvds-szfpga.fs oddr-tlvds-szfpga.fs \ @@ -314,6 +314,7 @@ bsram-%-tangnano1k-synth.json: pll/GW1NZ-1-dyn.vh %-image-rom.v %-video-ram.v %. %-pll-tangnano4k-synth.json: pll/GW1NS-4-dyn.vh %-pll-vr.v $(YOSYS) -D INV_BTN=0 -D LEDS_NR=6 -p "read_verilog $^; synth_gowin -json $@" + # ============================================================ # Tangnano9k (GW1N-9C) %-tangnano9k.fs: %-tangnano9k.json @@ -323,7 +324,7 @@ bsram-%-tangnano1k-synth.json: pll/GW1NZ-1-dyn.vh %-image-rom.v %-video-ram.v %. $(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88PC6/I5 --vopt family=GW1N-9C --vopt cst=tangnano9k.cst %-tangnano9k-synth.json: %.v - $(YOSYS) -D LEDS_NR=6 -D OSC_TYPE_OSC -D INV_BTN=0 -D CPU_FREQ=27 -D BAUD_RATE=115200 -D NUM_HCLK=5 -p "read_verilog $^; synth_gowin -json $@" + $(YOSYS) -D LEDS_NR=6 -D OSC_TYPE_OSC -D INV_BTN=0 -D CPU_FREQ=27 -D BAUD_RATE=115200 -D NUM_HCLK=5 -D HAS_FLASH608K -p "read_verilog $^; synth_gowin -json $@" pll-nanolcd-tangnano9k-synth.json: pll/GW1N-9C-dyn.vh pll-nanolcd/TOP.v pll-nanolcd/VGAMod.v $(YOSYS) -D INV_BTN=0 -p "read_verilog $^; synth_gowin -json $@" diff --git a/examples/himbaechel/dsp-alu54d.v b/examples/himbaechel/dsp-alu54d.v index 3fa8b141..36cf2d21 100644 --- a/examples/himbaechel/dsp-alu54d.v +++ b/examples/himbaechel/dsp-alu54d.v @@ -96,6 +96,6 @@ ALU54D alu2( defparam alu2.ALU_RESET_MODE="SYNC"; endmodule -`define FIRMWARE "riscv-dsp-firmware/alu54d.hex" +`define FIRMWARE "riscv-firmware/alu54d.hex" `include "dsp-riscv.v" diff --git a/examples/himbaechel/dsp-mult18x18.v b/examples/himbaechel/dsp-mult18x18.v index 7b74a270..dc999c8a 100644 --- a/examples/himbaechel/dsp-mult18x18.v +++ b/examples/himbaechel/dsp-mult18x18.v @@ -143,6 +143,6 @@ defparam mult_4.SOA_REG=1'b0; defparam mult_4.MULT_RESET_MODE="SYNC"; endmodule -`define FIRMWARE "riscv-dsp-firmware/mult18x18.hex" +`define FIRMWARE "riscv-firmware/mult18x18.hex" `include "dsp-riscv.v" diff --git a/examples/himbaechel/dsp-mult36x36.v b/examples/himbaechel/dsp-mult36x36.v index 7181829f..f40693c7 100644 --- a/examples/himbaechel/dsp-mult36x36.v +++ b/examples/himbaechel/dsp-mult36x36.v @@ -49,6 +49,6 @@ module idsp(input wire clk, input wire reset, defparam mu_1.MULT_RESET_MODE="SYNC"; endmodule -`define FIRMWARE "riscv-dsp-firmware/mult36x36.hex" +`define FIRMWARE "riscv-firmware/mult36x36.hex" `include "dsp-riscv.v" diff --git a/examples/himbaechel/dsp-mult9x9.v b/examples/himbaechel/dsp-mult9x9.v index 3622f24f..678426c5 100644 --- a/examples/himbaechel/dsp-mult9x9.v +++ b/examples/himbaechel/dsp-mult9x9.v @@ -146,6 +146,6 @@ defparam mult_4.SOA_REG=1'b0; defparam mult_4.MULT_RESET_MODE="SYNC"; endmodule -`define FIRMWARE "riscv-dsp-firmware/mult9x9.hex" +`define FIRMWARE "riscv-firmware/mult9x9.hex" `include "dsp-riscv.v" diff --git a/examples/himbaechel/dsp-multaddalu18x18.v b/examples/himbaechel/dsp-multaddalu18x18.v index c0c15a13..106eb965 100644 --- a/examples/himbaechel/dsp-multaddalu18x18.v +++ b/examples/himbaechel/dsp-multaddalu18x18.v @@ -186,6 +186,6 @@ module idsp(input wire clk, input wire reset, defparam multaddalu_3.MULTADDALU18X18_MODE=2; endmodule -`define FIRMWARE "riscv-dsp-firmware/multaddalu18x18.hex" +`define FIRMWARE "riscv-firmware/multaddalu18x18.hex" `include "dsp-riscv.v" diff --git a/examples/himbaechel/dsp-multalu18x18.v b/examples/himbaechel/dsp-multalu18x18.v index b40e17fe..8684f0b9 100644 --- a/examples/himbaechel/dsp-multalu18x18.v +++ b/examples/himbaechel/dsp-multalu18x18.v @@ -177,6 +177,6 @@ module idsp(input wire clk, input wire reset, defparam multalu_4.MULTALU18X18_MODE=2; endmodule -`define FIRMWARE "riscv-dsp-firmware/multalu18x18.hex" +`define FIRMWARE "riscv-firmware/multalu18x18.hex" `include "dsp-riscv.v" diff --git a/examples/himbaechel/dsp-multalu36x18.v b/examples/himbaechel/dsp-multalu36x18.v index a7f29192..dc9b712b 100644 --- a/examples/himbaechel/dsp-multalu36x18.v +++ b/examples/himbaechel/dsp-multalu36x18.v @@ -152,6 +152,6 @@ module idsp(input wire clk, input wire reset, defparam multalu_4.MULTALU36X18_MODE=0; endmodule -`define FIRMWARE "riscv-dsp-firmware/multalu36x18.hex" +`define FIRMWARE "riscv-firmware/multalu36x18.hex" `include "dsp-riscv.v" diff --git a/examples/himbaechel/dsp-padd18.v b/examples/himbaechel/dsp-padd18.v index cd262702..03fee5b5 100644 --- a/examples/himbaechel/dsp-padd18.v +++ b/examples/himbaechel/dsp-padd18.v @@ -119,6 +119,6 @@ module idsp(input wire clk, input wire reset, defparam padd18_4.PADD_RESET_MODE="SYNC"; endmodule -`define FIRMWARE "riscv-dsp-firmware/padd18.hex" +`define FIRMWARE "riscv-firmware/padd18.hex" `include "dsp-riscv.v" diff --git a/examples/himbaechel/dsp-padd9.v b/examples/himbaechel/dsp-padd9.v index 1a99a45b..a7bce84f 100644 --- a/examples/himbaechel/dsp-padd9.v +++ b/examples/himbaechel/dsp-padd9.v @@ -121,6 +121,6 @@ module idsp(input wire clk, input wire reset, defparam padd_4.PADD_RESET_MODE="SYNC"; endmodule -`define FIRMWARE "riscv-dsp-firmware/padd9.hex" +`define FIRMWARE "riscv-firmware/padd9.hex" `include "dsp-riscv.v" diff --git a/examples/himbaechel/femto-riscv-userflash.v b/examples/himbaechel/femto-riscv-userflash.v new file mode 100644 index 00000000..f489dab9 --- /dev/null +++ b/examples/himbaechel/femto-riscv-userflash.v @@ -0,0 +1,446 @@ +/** + * Step 18: Creating a RISC-V processor + * Mandelbrot in the terminal + * + * Copyright (c) 2020, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +`default_nettype none +`include "clockworks.v" +`include "emitter_uart.v" +`include "uflash_controller.v" + +module Memory ( + input clk, + input [31:0] mem_addr, // address to be read + output reg [31:0] mem_rdata, // data read from memory + input mem_rstrb, // goes high when processor wants to read + input [31:0] mem_wdata, // data to be written + input [3:0] mem_wmask // masks for writing the 4 bytes (1=write byte) +); + + reg [31:0] MEM [0:1535]; // 1536 4-bytes words = 6 Kb of RAM in total + + initial begin + $readmemh("riscv-firmware/userflash-9k.hex", MEM); + end + + localparam slow_bit=19; + + wire [29:0] word_addr = mem_addr[31:2]; + + always @(posedge clk) begin + if(mem_rstrb) begin + mem_rdata <= MEM[word_addr]; + end + if(mem_wmask[0]) MEM[word_addr][ 7:0 ] <= mem_wdata[ 7:0 ]; + if(mem_wmask[1]) MEM[word_addr][15:8 ] <= mem_wdata[15:8 ]; + if(mem_wmask[2]) MEM[word_addr][23:16] <= mem_wdata[23:16]; + if(mem_wmask[3]) MEM[word_addr][31:24] <= mem_wdata[31:24]; + end +endmodule + +module Processor ( + input clk, + input resetn, + output [31:0] mem_addr, + input [31:0] mem_rdata, + input wire mem_rready, + output mem_rstrb, + output [31:0] mem_wdata, + output [3:0] mem_wmask +); + + reg [31:0] PC=0; // program counter + reg [31:0] instr; // current instruction + + // See the table P. 105 in RISC-V manual + + // The 10 RISC-V instructions + wire isALUreg = (instr[6:0] == 7'b0110011); // rd <- rs1 OP rs2 + wire isALUimm = (instr[6:0] == 7'b0010011); // rd <- rs1 OP Iimm + wire isBranch = (instr[6:0] == 7'b1100011); // if(rs1 OP rs2) PC<-PC+Bimm + wire isJALR = (instr[6:0] == 7'b1100111); // rd <- PC+4; PC<-rs1+Iimm + wire isJAL = (instr[6:0] == 7'b1101111); // rd <- PC+4; PC<-PC+Jimm + wire isAUIPC = (instr[6:0] == 7'b0010111); // rd <- PC + Uimm + wire isLUI = (instr[6:0] == 7'b0110111); // rd <- Uimm + wire isLoad = (instr[6:0] == 7'b0000011); // rd <- mem[rs1+Iimm] + wire isStore = (instr[6:0] == 7'b0100011); // mem[rs1+Simm] <- rs2 + wire isSYSTEM = (instr[6:0] == 7'b1110011); // special + + // The 5 immediate formats + wire [31:0] Uimm={ instr[31], instr[30:12], {12{1'b0}}}; + wire [31:0] Iimm={{21{instr[31]}}, instr[30:20]}; + wire [31:0] Simm={{21{instr[31]}}, instr[30:25],instr[11:7]}; + wire [31:0] Bimm={{20{instr[31]}}, instr[7],instr[30:25],instr[11:8],1'b0}; + wire [31:0] Jimm={{12{instr[31]}}, instr[19:12],instr[20],instr[30:21],1'b0}; + + // Source and destination registers + wire [4:0] rs1Id = instr[19:15]; + wire [4:0] rs2Id = instr[24:20]; + wire [4:0] rdId = instr[11:7]; + + // function codes + wire [2:0] funct3 = instr[14:12]; + wire [6:0] funct7 = instr[31:25]; + + // The registers bank +`ifdef FORCE_BRAM + (* ram_style = "block" *) +`endif + reg [31:0] RegisterBank [0:31]; + reg [31:0] rs1; // value of source + reg [31:0] rs2; // registers. + wire [31:0] writeBackData; // data to be written to rd + wire writeBackEn; // asserted if data should be written to rd + + // The ALU + wire [31:0] aluIn1 = rs1; + wire [31:0] aluIn2 = isALUreg | isBranch ? rs2 : Iimm; + + wire [4:0] shamt = isALUreg ? rs2[4:0] : instr[24:20]; // shift amount + + // The adder is used by both arithmetic instructions and JALR. + wire [31:0] aluPlus = aluIn1 + aluIn2; + + // Use a single 33 bits subtract to do subtraction and all comparisons + // (trick borrowed from swapforth/J1) + wire [32:0] aluMinus = {1'b1, ~aluIn2} + {1'b0,aluIn1} + 33'b1; + wire LT = (aluIn1[31] ^ aluIn2[31]) ? aluIn1[31] : aluMinus[32]; + wire LTU = aluMinus[32]; + wire EQ = (aluMinus[31:0] == 0); + + // Flip a 32 bit word. Used by the shifter (a single shifter for + // left and right shifts, saves silicium !) + function [31:0] flip32; + input [31:0] x; + flip32 = {x[ 0], x[ 1], x[ 2], x[ 3], x[ 4], x[ 5], x[ 6], x[ 7], + x[ 8], x[ 9], x[10], x[11], x[12], x[13], x[14], x[15], + x[16], x[17], x[18], x[19], x[20], x[21], x[22], x[23], + x[24], x[25], x[26], x[27], x[28], x[29], x[30], x[31]}; + endfunction + + wire [31:0] shifter_in = (funct3 == 3'b001) ? flip32(aluIn1) : aluIn1; + + /* verilator lint_off WIDTH */ + wire [31:0] shifter = + $signed({instr[30] & aluIn1[31], shifter_in}) >>> aluIn2[4:0]; + /* verilator lint_on WIDTH */ + + wire [31:0] leftshift = flip32(shifter); + + + + // ADD/SUB/ADDI: + // funct7[5] is 1 for SUB and 0 for ADD. We need also to test instr[5] + // to make the difference with ADDI + // + // SRLI/SRAI/SRL/SRA: + // funct7[5] is 1 for arithmetic shift (SRA/SRAI) and + // 0 for logical shift (SRL/SRLI) + reg [31:0] aluOut; + always @(*) begin + case(funct3) + 3'b000: aluOut = (funct7[5] & instr[5]) ? aluMinus[31:0] : aluPlus; + 3'b001: aluOut = leftshift; + 3'b010: aluOut = {31'b0, LT}; + 3'b011: aluOut = {31'b0, LTU}; + 3'b100: aluOut = (aluIn1 ^ aluIn2); + 3'b101: aluOut = shifter; + 3'b110: aluOut = (aluIn1 | aluIn2); + 3'b111: aluOut = (aluIn1 & aluIn2); + endcase + end + + // The predicate for branch instructions + reg takeBranch; + always @(*) begin + case(funct3) + 3'b000: takeBranch = EQ; + 3'b001: takeBranch = !EQ; + 3'b100: takeBranch = LT; + 3'b101: takeBranch = !LT; + 3'b110: takeBranch = LTU; + 3'b111: takeBranch = !LTU; + default: takeBranch = 1'b0; + endcase + end + + + // Address computation + // An adder used to compute branch address, JAL address and AUIPC. + // branch->PC+Bimm AUIPC->PC+Uimm JAL->PC+Jimm + // Equivalent to PCplusImm = PC + (isJAL ? Jimm : isAUIPC ? Uimm : Bimm) + wire [31:0] PCplusImm = PC + ( instr[3] ? Jimm[31:0] : + instr[4] ? Uimm[31:0] : + Bimm[31:0] ); + wire [31:0] PCplus4 = PC+4; + + // register write back + assign writeBackData = (isJAL || isJALR) ? PCplus4 : + isLUI ? Uimm : + isAUIPC ? PCplusImm : + isLoad ? LOAD_data : + aluOut; + + wire [31:0] nextPC = ((isBranch && takeBranch) || isJAL) ? PCplusImm : + isJALR ? {aluPlus[31:1],1'b0} : + PCplus4; + + wire [31:0] loadstore_addr = rs1 + (isStore ? Simm : Iimm); + + // Load + // All memory accesses are aligned on 32 bits boundary. For this + // reason, we need some circuitry that does unaligned halfword + // and byte load/store, based on: + // - funct3[1:0]: 00->byte 01->halfword 10->word + // - mem_addr[1:0]: indicates which byte/halfword is accessed + + wire mem_byteAccess = funct3[1:0] == 2'b00; + wire mem_halfwordAccess = funct3[1:0] == 2'b01; + + + wire [15:0] LOAD_halfword = + loadstore_addr[1] ? mem_rdata[31:16] : mem_rdata[15:0]; + + wire [7:0] LOAD_byte = + loadstore_addr[0] ? LOAD_halfword[15:8] : LOAD_halfword[7:0]; + + // LOAD, in addition to funct3[1:0], LOAD depends on: + // - funct3[2] (instr[14]): 0->do sign expansion 1->no sign expansion + wire LOAD_sign = + !funct3[2] & (mem_byteAccess ? LOAD_byte[7] : LOAD_halfword[15]); + + wire [31:0] LOAD_data = + mem_byteAccess ? {{24{LOAD_sign}}, LOAD_byte} : + mem_halfwordAccess ? {{16{LOAD_sign}}, LOAD_halfword} : + mem_rdata ; + + // Store + // ------------------------------------------------------------------------ + + assign mem_wdata[ 7: 0] = rs2[7:0]; + assign mem_wdata[15: 8] = loadstore_addr[0] ? rs2[7:0] : rs2[15: 8]; + assign mem_wdata[23:16] = loadstore_addr[1] ? rs2[7:0] : rs2[23:16]; + assign mem_wdata[31:24] = loadstore_addr[0] ? rs2[7:0] : + loadstore_addr[1] ? rs2[15:8] : rs2[31:24]; + + // The memory write mask: + // 1111 if writing a word + // 0011 or 1100 if writing a halfword + // (depending on loadstore_addr[1]) + // 0001, 0010, 0100 or 1000 if writing a byte + // (depending on loadstore_addr[1:0]) + + wire [3:0] STORE_wmask = + mem_byteAccess ? + (loadstore_addr[1] ? + (loadstore_addr[0] ? 4'b1000 : 4'b0100) : + (loadstore_addr[0] ? 4'b0010 : 4'b0001) + ) : + mem_halfwordAccess ? + (loadstore_addr[1] ? 4'b1100 : 4'b0011) : + 4'b1111; + + // The state machine + localparam FETCH_INSTR = 0; + localparam WAIT_INSTR = 1; + localparam FETCH_REGS = 2; + localparam EXECUTE = 3; + localparam LOAD = 4; + localparam WAIT_DATA = 5; + localparam STORE = 6; + reg [2:0] state = FETCH_INSTR; + + always @(posedge clk) begin + if(!resetn) begin + PC <= 0; + state <= FETCH_INSTR; + end else begin + if(writeBackEn && rdId != 0) begin + RegisterBank[rdId] <= writeBackData; + end + case(state) + FETCH_INSTR: begin + state <= WAIT_INSTR; + end + WAIT_INSTR: begin + instr <= mem_rdata; + state <= FETCH_REGS; + end + FETCH_REGS: begin + rs1 <= RegisterBank[rs1Id]; + rs2 <= RegisterBank[rs2Id]; + state <= EXECUTE; + end + EXECUTE: begin + if(!isSYSTEM) begin + PC <= nextPC; + end + state <= isLoad ? LOAD : + isStore ? STORE : + FETCH_INSTR; + end + LOAD: begin + state <= WAIT_DATA; + end + WAIT_DATA: begin + if (mem_rready) begin + state <= FETCH_INSTR; + end + end + STORE: begin + state <= WAIT_DATA; + //state <= FETCH_INSTR; + end + endcase + end + end + + assign writeBackEn = (state==EXECUTE && !isBranch && !isStore) || + (state==WAIT_DATA) ; + + assign mem_addr = (state == WAIT_INSTR || state == FETCH_INSTR) ? + PC : loadstore_addr ; + assign mem_rstrb = (state == FETCH_INSTR || state == LOAD); + assign mem_wmask = {4{(state == STORE)}} & STORE_wmask; + +endmodule + + +module top ( + input clk_i, // system clock + input rst_i, // reset button + output [5:0] led, // system LEDs + input RXD, // UART receive + output TXD // UART transmit +); + + wire clk; + wire resetn; + + wire [31:0] mem_addr; + wire [31:0] mem_rdata; + wire mem_rready; + wire mem_rstrb; + wire [31:0] mem_wdata; + wire [3:0] mem_wmask; + + Processor CPU( + .clk(clk), + .resetn(resetn), + .mem_addr(mem_addr), + .mem_rdata(mem_rdata), + .mem_rready(mem_rready), + .mem_rstrb(mem_rstrb), + .mem_wdata(mem_wdata), + .mem_wmask(mem_wmask) + ); + + wire [31:0] RAM_rdata; + wire [29:0] mem_wordaddr = mem_addr[31:2]; + wire isUserFlash = mem_addr[23:22] == 2'b10; + wire isIO = mem_addr[23:22] == 2'b01; + wire isRAM = mem_addr[23:22] == 2'b00; + wire mem_wstrb = |mem_wmask; + + Memory RAM( + .clk(clk), + .mem_addr(mem_addr), + .mem_rdata(RAM_rdata), + .mem_rstrb(isRAM & mem_rstrb), + .mem_wdata(mem_wdata), + .mem_wmask({4{isRAM}}&mem_wmask) + ); + + // UserFlash + wire [31:0] UserFlash_rdata; + wire UserFlash_ready; + wire UserFlash_strob = isUserFlash & (mem_wstrb | mem_rstrb); + uflash UserFlash( + .reset_n(resetn), + .clk(clk), + .sel(UserFlash_strob), + .wstrb(mem_wmask), + .addr(mem_wordaddr[14:0]), + .data_i(mem_wdata), + .ready(UserFlash_ready), + .data_o(UserFlash_rdata) + ); + defparam UserFlash.CLK_FREQ=`CPU_FREQ*1000000; + + // Memory-mapped IO in IO page, 1-hot addressing in word address. + localparam IO_LEDS_bit = 0; // W five leds + localparam IO_UART_DAT_bit = 1; // W data to send (8 bits) + localparam IO_UART_CNTL_bit = 2; // R status. bit 9: busy sending + + always @(posedge clk) begin + if(isIO & mem_wstrb & mem_wordaddr[IO_LEDS_bit]) begin + led <= mem_wdata; + end + end + + wire uart_valid = isIO & mem_wstrb & mem_wordaddr[IO_UART_DAT_bit]; + wire uart_ready; + + corescore_emitter_uart #( + .clk_freq_hz(`CPU_FREQ*1000000), + .baud_rate(`BAUD_RATE) + ) UART( + .i_clk(clk), + .i_rst(!resetn), + .i_data(mem_wdata[7:0]), + .i_valid(uart_valid), + .o_ready(uart_ready), + .o_uart_tx(TXD) + ); + + wire [31:0] IO_rdata = + mem_wordaddr[IO_UART_CNTL_bit] ? { 22'b0, !uart_ready, 9'b0} + : 32'b0; + + assign mem_rdata = isRAM ? RAM_rdata : + isUserFlash ? UserFlash_rdata : + IO_rdata ; + assign mem_rready = isUserFlash ? UserFlash_ready : 1'b1; + + + // Gearbox and reset circuitry. + Clockworks #( + .SLOW(0) //Specifying a value other than zero here may result in + // nextpnr being unable to route the clock for some boards. BUGFIX is under development. + ) CW ( + .CLK(clk_i), + .RESET(rst_i), + .clk(clk), + .resetn(resetn) + ); + +endmodule + diff --git a/examples/himbaechel/riscv-dsp-firmware/README.md b/examples/himbaechel/riscv-firmware/README.md similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/README.md rename to examples/himbaechel/riscv-firmware/README.md diff --git a/examples/himbaechel/riscv-dsp-firmware/alu54d.hex b/examples/himbaechel/riscv-firmware/alu54d.hex similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/alu54d.hex rename to examples/himbaechel/riscv-firmware/alu54d.hex diff --git a/examples/himbaechel/riscv-dsp-firmware/mult18x18.S b/examples/himbaechel/riscv-firmware/mult18x18.S similarity index 99% rename from examples/himbaechel/riscv-dsp-firmware/mult18x18.S rename to examples/himbaechel/riscv-firmware/mult18x18.S index 731ffc97..409db11f 100644 --- a/examples/himbaechel/riscv-dsp-firmware/mult18x18.S +++ b/examples/himbaechel/riscv-firmware/mult18x18.S @@ -82,7 +82,7 @@ print_2_bytes: srli a0, a0, 8 call print_byte_hex - andi s0, s1, 0xff + andi a0, s1, 0xff call print_byte_hex lw s1, 0(sp) diff --git a/examples/himbaechel/riscv-dsp-firmware/mult18x18.hex b/examples/himbaechel/riscv-firmware/mult18x18.hex similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/mult18x18.hex rename to examples/himbaechel/riscv-firmware/mult18x18.hex diff --git a/examples/himbaechel/riscv-dsp-firmware/mult36x36.S b/examples/himbaechel/riscv-firmware/mult36x36.S similarity index 99% rename from examples/himbaechel/riscv-dsp-firmware/mult36x36.S rename to examples/himbaechel/riscv-firmware/mult36x36.S index 3b211ece..6a96c341 100644 --- a/examples/himbaechel/riscv-dsp-firmware/mult36x36.S +++ b/examples/himbaechel/riscv-firmware/mult36x36.S @@ -82,7 +82,7 @@ print_2_bytes: srli a0, a0, 8 call print_byte_hex - andi s0, s1, 0xff + andi a0, s1, 0xff call print_byte_hex lw s1, 0(sp) diff --git a/examples/himbaechel/riscv-dsp-firmware/mult36x36.hex b/examples/himbaechel/riscv-firmware/mult36x36.hex similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/mult36x36.hex rename to examples/himbaechel/riscv-firmware/mult36x36.hex diff --git a/examples/himbaechel/riscv-dsp-firmware/mult9x9.S b/examples/himbaechel/riscv-firmware/mult9x9.S similarity index 99% rename from examples/himbaechel/riscv-dsp-firmware/mult9x9.S rename to examples/himbaechel/riscv-firmware/mult9x9.S index 0aa4dbad..344d1dc8 100644 --- a/examples/himbaechel/riscv-dsp-firmware/mult9x9.S +++ b/examples/himbaechel/riscv-firmware/mult9x9.S @@ -82,7 +82,7 @@ print_2_bytes: srli a0, a0, 8 call print_byte_hex - andi s0, s1, 0xff + andi a0, s1, 0xff call print_byte_hex lw s1, 0(sp) diff --git a/examples/himbaechel/riscv-dsp-firmware/mult9x9.hex b/examples/himbaechel/riscv-firmware/mult9x9.hex similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/mult9x9.hex rename to examples/himbaechel/riscv-firmware/mult9x9.hex diff --git a/examples/himbaechel/riscv-dsp-firmware/multaddalu18x18.hex b/examples/himbaechel/riscv-firmware/multaddalu18x18.hex similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/multaddalu18x18.hex rename to examples/himbaechel/riscv-firmware/multaddalu18x18.hex diff --git a/examples/himbaechel/riscv-dsp-firmware/multalu18x18.hex b/examples/himbaechel/riscv-firmware/multalu18x18.hex similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/multalu18x18.hex rename to examples/himbaechel/riscv-firmware/multalu18x18.hex diff --git a/examples/himbaechel/riscv-dsp-firmware/multalu36x18.hex b/examples/himbaechel/riscv-firmware/multalu36x18.hex similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/multalu36x18.hex rename to examples/himbaechel/riscv-firmware/multalu36x18.hex diff --git a/examples/himbaechel/riscv-dsp-firmware/padd18.S b/examples/himbaechel/riscv-firmware/padd18.S similarity index 99% rename from examples/himbaechel/riscv-dsp-firmware/padd18.S rename to examples/himbaechel/riscv-firmware/padd18.S index 8ea7ef7c..4ffaf1cb 100644 --- a/examples/himbaechel/riscv-dsp-firmware/padd18.S +++ b/examples/himbaechel/riscv-firmware/padd18.S @@ -82,7 +82,7 @@ print_2_bytes: srli a0, a0, 8 call print_byte_hex - andi s0, s1, 0xff + andi a0, s1, 0xff call print_byte_hex lw s1, 0(sp) diff --git a/examples/himbaechel/riscv-dsp-firmware/padd18.hex b/examples/himbaechel/riscv-firmware/padd18.hex similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/padd18.hex rename to examples/himbaechel/riscv-firmware/padd18.hex diff --git a/examples/himbaechel/riscv-dsp-firmware/padd9.S b/examples/himbaechel/riscv-firmware/padd9.S similarity index 99% rename from examples/himbaechel/riscv-dsp-firmware/padd9.S rename to examples/himbaechel/riscv-firmware/padd9.S index 57432faa..b727d8d1 100644 --- a/examples/himbaechel/riscv-dsp-firmware/padd9.S +++ b/examples/himbaechel/riscv-firmware/padd9.S @@ -82,7 +82,7 @@ print_2_bytes: srli a0, a0, 8 call print_byte_hex - andi s0, s1, 0xff + andi a0, s1, 0xff call print_byte_hex lw s1, 0(sp) diff --git a/examples/himbaechel/riscv-dsp-firmware/padd9.hex b/examples/himbaechel/riscv-firmware/padd9.hex similarity index 100% rename from examples/himbaechel/riscv-dsp-firmware/padd9.hex rename to examples/himbaechel/riscv-firmware/padd9.hex diff --git a/examples/himbaechel/riscv-firmware/userflash-9k.hex b/examples/himbaechel/riscv-firmware/userflash-9k.hex new file mode 100644 index 00000000..a5ed1b9c --- /dev/null +++ b/examples/himbaechel/riscv-firmware/userflash-9k.hex @@ -0,0 +1,384 @@ +004001b7 00002137 80010113 00000097 +164080e7 00100073 00a1a423 20000293 +0101a303 00537333 fe031ce3 00008067 +ffc10113 00112023 00050393 0003c503 +00050a63 00000097 fd4080e7 00138393 +fedff06f 00012083 00410113 00008067 +ffc10113 00112023 00a00513 00000097 +fac080e7 00012083 00410113 00008067 +33323130 37363534 62613938 66656463 +ffc10113 00112023 00000f17 fe8f0f13 +00050e13 00455513 00af0eb3 000ec503 +00000097 f68080e7 00fe7513 00af0eb3 +000ec503 00000097 f54080e7 00012083 +00410113 00008067 ff810113 00112223 +00912023 00050493 00855513 00000097 +fa4080e7 0ff4f513 00000097 f98080e7 +00012483 00412083 00810113 00008067 +ff810113 00112223 00912023 00050493 +01055513 00000097 fb4080e7 000102b7 +fff28293 0054f533 00000097 fa0080e7 +00012483 00412083 00810113 00008067 +00dd9513 00adcdb3 011dd513 00adcdb3 +005d9513 00adcdb3 000d8513 00008067 +00800937 00000517 13850513 00000097 +eb4080e7 00000097 edc080e7 00000517 +19750513 00000097 e9c080e7 00000097 +ec4080e7 0000ddb7 afed8d93 00000993 +00013a37 01298b33 7ff9f313 00031a63 +00ab0023 02e00513 00000097 e50080e7 +20000a93 00000097 f7c080e7 00ab2023 +00498993 fffa8a93 01298b33 fe0a94e3 +fd4994e3 00000097 e6c080e7 00000517 +15650513 00000097 e2c080e7 00000097 +e54080e7 0000ddb7 afed8d93 00000993 +13000a13 00699513 00000097 ee8080e7 +00000517 19250513 00000097 df8080e7 +00000a93 04000b13 00000097 f08080e7 +00050313 00699293 015282b3 012282b3 +0002a283 02e00513 00628463 05800513 +00000097 da8080e7 00000097 da0080e7 +00000097 d98080e7 00000097 d90080e7 +004a8a93 fb6a9ae3 00000097 dc8080e7 +00198993 f94990e3 0000006f 63697041 +2e616c75 6d695320 20656c70 72657355 +73616c46 65742068 203a7473 73617245 +76652065 74797265 676e6968 7277202c +20657469 73702061 6f647565 6e61722d +206d6f64 626d756e 74207265 6165206f +61206863 65726464 202c7373 20646e61 +6e656874 61657220 74692064 63616220 +45002e6b 65736172 67617020 79622065 +67617020 6e612065 72772064 20657469 +646e6172 6e206d6f 65626d75 2e2e7372 +6552002e 74206461 65206568 7269746e +73552065 6c467265 2c687361 63616520 +2e272068 65722027 73657270 73746e65 +6d206120 68637461 74697720 68742068 +20342065 65747962 72772073 65747469 +65202c6e 20686361 20275827 72706572 +6e657365 61207374 69616620 6572756c +203a002e 00010000 00001941 73697200 +01007663 0000000f 33767205 70326932 +00000030 00100293 01329293 fff28293 +fe029ee3 00008067 00001941 73697200 +01007663 0000000f 33767205 70326932 +19410030 72000000 76637369 000f0100 +72050000 69323376 00307032 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 diff --git a/examples/himbaechel/riscv-firmware/userflash.S b/examples/himbaechel/riscv-firmware/userflash.S new file mode 100644 index 00000000..fa0cb0bf --- /dev/null +++ b/examples/himbaechel/riscv-firmware/userflash.S @@ -0,0 +1,192 @@ +.equ IO_BASE, 0x400000 +.equ IO_LEDS, 4 +.equ IO_UART_DAT, 8 +.equ IO_UART_CTRL, 16 +.equ USERFLASH_BASE, 0x800000 + +.section .text + +putc: + sw a0, IO_UART_DAT(gp) + li t0, 512 +0: + lw t1, IO_UART_CTRL(gp) + and t1, t1, t0 + bnez t1, 0b + ret + +puts: + addi sp, sp, -4 + sw ra, 0(sp) + mv t2, a0 +0: + lbu a0, 0(t2) + beqz a0, 1f + call putc + addi t2, t2, 1 + j 0b +1: + lw ra, 0(sp) + addi sp, sp, 4 + ret + +put_eol: + addi sp, sp, -4 + sw ra, 0(sp) + + li a0, 0xa + call putc + + lw ra, 0(sp) + addi sp, sp, 4 + ret + +hex: .ascii "0123456789abcdef" +print_byte_hex: + addi sp, sp, -4 + sw ra, 0(sp) + + la t5, hex + mv t3, a0 + + srli a0, a0, 4 + add t4, t5, a0 + lbu a0, 0(t4) + call putc + + andi a0, t3, 0xf + add t4, t5, a0 + lbu a0, 0(t4) + call putc + + lw ra, 0(sp) + addi sp, sp, 4 + ret + +print_2_bytes: + addi sp, sp, -8 + sw ra, 4(sp) + sw s1, 0(sp) + + mv s1, a0 + srli a0, a0, 8 + call print_byte_hex + + andi a0, s1, 0xff + call print_byte_hex + + lw s1, 0(sp) + lw ra, 4(sp) + addi sp, sp, 8 + ret + +print_4_bytes: + addi sp, sp, -8 + sw ra, 4(sp) + sw s1, 0(sp) + + mv s1, a0 + srli a0, a0, 16 + call print_2_bytes + + li t0, 0xffff + and a0, s1, t0 + call print_2_bytes + + lw s1, 0(sp) + lw ra, 4(sp) + addi sp, sp, 8 + ret + +.globl main +rnd: # xorshift rnd generator + slli a0, s11, 13 + xor s11, s11, a0 + srli a0, s11, 17 + xor s11, s11, a0 + slli a0, s11, 5 + xor s11, s11, a0 + mv a0, s11 + ret +main: + li s2, USERFLASH_BASE + la a0, hello_str + call puts + call put_eol + + # Erase/Write + la a0, erase_write_title + call puts + call put_eol + li s11, 0xcafe # init rnd + + mv s3, zero + li s4, 304*64*4 + add s6, s3, s2 +.L20: + andi t1, s3, 2047 # 2048 bytes per page + bnez t1, .L21 + sb a0, (s6) # writing byte erases page + li a0, '.' + call putc +.L21: + li s5, 512 # 2048/4 +.L22: + call rnd + sw a0, (s6) + add s3, s3, 4 + add s5, s5, -1 + add s6, s3, s2 + bnez s5, .L22 + bne s3, s4, .L20 + + call put_eol + + # Read back + la a0, read_title + call puts + call put_eol + li s11, 0xcafe # init rnd + mv s3, zero + li s4, 304 +.L1: + slli a0, s3, 6 + call print_4_bytes + la a0, colon_space + call puts + + mv s5, zero + li s6, 64 +.L0: + # read flash + call rnd + mv t1, a0 + slli t0, s3, 6 + add t0, t0, s5 + add t0, t0, s2 + lw t0, (t0) + li a0, '.' + beq t0, t1, .L2 + li a0, 'X' +.L2: + call putc + call putc + call putc + call putc + + add s5, s5, 4 + bne s5, s6, .L0 + + call put_eol + + add s3, s3, 1 + bne s3, s4, .L1 + + +.L10: + j .L10 + +hello_str: .asciz "Apicula. Simple UserFlash test: Erase everything, write a pseudo-random number to each address, and then read it back." +erase_write_title: .asciz "Erase page by page and write random numbers..." +read_title: .asciz "Read the entire UserFlash, each '.' represents a match with the 4 bytes written, each 'X' represents a failure." +colon_space: .asciz ": " diff --git a/examples/himbaechel/tangnano9k.cst b/examples/himbaechel/tangnano9k.cst index 6f5a33b7..a4a655f8 100644 --- a/examples/himbaechel/tangnano9k.cst +++ b/examples/himbaechel/tangnano9k.cst @@ -18,9 +18,7 @@ IO_LOC "LED_B" 13; // Use buildin USB-serial IO_LOC "TXD" 17; -IO_PORT "TXD" PULL_MODE=UP; IO_LOC "RXD" 18; -IO_PORT "RXD" PULL_MODE=UP; // fake IO_LOC "led[6]" 35; diff --git a/examples/himbaechel/uflash_controller.v b/examples/himbaechel/uflash_controller.v new file mode 100644 index 00000000..04446af7 --- /dev/null +++ b/examples/himbaechel/uflash_controller.v @@ -0,0 +1,283 @@ +/* Copyright 2024 Grug Huhler. License SPDX BSD-2-Clause. + + This module implements a controller for the user flash on the Tang + Nano 9K FPGA development board. The controller integrates with + the PicoRV32 mini-SoC bus scheme. It also instantiates the + actual flash. Note: the Tang Nano 20K does not contain user + flash. + + See document UG295 "Gowin User Flash". + + The Flash is 608 Kbits, 32-bits wide, organized into 304 rows of 64 + columns each. The erase page size is 2048 bytes, so there are + 38 pages that may be separately erased. + + This controller expects a system clock no more than 40 Mhz. The + actual clock frequency must be passed to the module via the + CLK_FREQ parameter. + + Leave at least 10 millisconds between a write and an erase and do + not write the same address twice without an erase between the writes. + The controller does not enforce these rules. + + Reads can be 8, 16, or 32 bits wide. Erasing is done on a page basis. + To erase a page, do an 8 bit write to a 32-bit aligned address in the + page. To program (write), do a 32-bit write to the address to be + programmed. +*/ + +module uflash #(parameter CLK_FREQ=5400000) +( + input wire reset_n, + input wire clk, + input wire sel, + input wire [3:0] wstrb, + input wire [14:0] addr, // word address, 9-bits row, 6 bits col + input wire [31:0] data_i, + output wire ready, + output wire [31:0] data_o +); + + // state machine states + localparam IDLE = 'd0; + localparam READ1 = 'd1; + localparam READ2 = 'd2; + localparam ERASE1 = 'd3; + localparam ERASE2 = 'd4; + localparam ERASE3 = 'd5; + localparam ERASE4 = 'd6; + localparam ERASE5 = 'd7; + localparam WRITE1 = 'd8; + localparam WRITE2 = 'd9; + localparam WRITE3 = 'd10; + localparam WRITE4 = 'd11; + localparam WRITE5 = 'd12; + localparam WRITE6 = 'd13; + localparam WRITE7 = 'd14; + localparam DONE = 'd15; + + // clocks required in state when > 1 + localparam E2_CLKS = $rtoi(CLK_FREQ * 6.0e-6) + 1; + localparam E3_CLKS = $rtoi(CLK_FREQ * 120.0e-3) + 1; + localparam E4_CLKS = $rtoi(CLK_FREQ * 6.0e-6) + 1; + localparam E5_CLKS = $rtoi(CLK_FREQ * 11.0e-6) + 1; + localparam W2_CLKS = $rtoi(CLK_FREQ * 6.0e-6) + 1; + localparam W3_CLKS = $rtoi(CLK_FREQ * 11.0e-6) + 1; + localparam W4_CLKS = $rtoi(CLK_FREQ * 16.0e-6) + 1; + localparam W6_CLKS = $rtoi(CLK_FREQ * 6.0e-6) + 1; + localparam W7_CLKS = $rtoi(CLK_FREQ * 11.0e-6) + 1; + + reg xe = 1'b0; + reg ye = 1'b0; + reg se = 1'b0; + reg erase = 1'b0; + reg nvstr = 1'b0; + reg prog = 1'b0; + reg [3:0] state = IDLE; + reg [23:0] cycle_count; + + assign ready = state == DONE; + + always @(posedge clk or negedge reset_n) + if (!reset_n) begin + state <= IDLE; + se <= 1'b0; + xe <= 1'b0; + ye <= 1'b0; + erase <= 1'b0; + nvstr <= 1'b0; + prog <= 1'b0; + cycle_count <= 'd0; + end + else + case (state) + IDLE: begin + if (sel) begin + if (wstrb == 'b0) begin + // Read + state <= READ1; + xe <= 1'b1; + ye <= 1'b1; + end + else if (&wstrb) begin + // Write + state <= WRITE1; + xe <= 1'b1; + end else if (wstrb == 'b1) begin + // Erase + ye <= 1'b0; + se <= 1'b0; + xe <= 1'b1; + erase <= 1'b0; + nvstr <= 1'b0; + state <= ERASE1; + end else begin + // Unsupported + state <= DONE; + end + end + else + state <= IDLE; + end + READ1: begin + se <= 1'b1; + state <= READ2; + end + READ2: begin + se <= 1'b0; + state <= DONE; + end + ERASE1: begin + state <= ERASE2; + cycle_count <= 'd0; + erase <= 1'b1; + end + ERASE2: begin + if (cycle_count < E2_CLKS) begin + state <= ERASE2; + cycle_count <= cycle_count + 1; + end + else begin + state <= ERASE3; + cycle_count <= 'd0; + nvstr <= 1'b1; + end + end + ERASE3: begin + if (cycle_count < E3_CLKS) begin + state <= ERASE3; + cycle_count <= cycle_count + 1; + end + else begin + state <= ERASE4; + cycle_count <= 'd0; + erase <= 1'b0; + end + end + ERASE4: begin + if (cycle_count < E4_CLKS) begin + state <= ERASE4; + cycle_count <= cycle_count + 1; + end + else begin + state <= ERASE5; + cycle_count <= 'd0; + nvstr <= 1'b0; + end + end + ERASE5: begin + if (cycle_count < E5_CLKS) begin + state <= ERASE5; + cycle_count <= cycle_count + 1; + end + else begin + state <= DONE; + cycle_count <= 'd0; + xe <= 1'b0; + end + end + WRITE1: begin + state <= WRITE2; + prog <= 1'b1; + end + WRITE2: begin + if (cycle_count < W2_CLKS) begin + state <= WRITE2; + cycle_count <= cycle_count + 1; + end + else begin + state <= WRITE3; + cycle_count <= 'd0; + nvstr <= 1'b1; + end + end + WRITE3: begin + if (cycle_count < W3_CLKS) begin + state <= WRITE3; + cycle_count <= cycle_count + 1; + end + else begin + state <= WRITE4; + cycle_count <= 'd0; + ye <= 1'b1; + end + end + WRITE4: begin + if (cycle_count < W4_CLKS) begin + state <= WRITE4; + cycle_count <= cycle_count + 1; + end + else begin + state <= WRITE5; + cycle_count <= 'd0; + ye <= 1'b0; + end + end + WRITE5: begin + state <= WRITE6; + prog <= 1'b0; + end + WRITE6: begin + if (cycle_count < W6_CLKS) begin + state <= WRITE6; + cycle_count <= cycle_count + 1; + end + else begin + state <= WRITE7; + cycle_count <= 'd0; + nvstr <= 1'b0; + end + end + WRITE7: begin + if (cycle_count < W7_CLKS) begin + state <= WRITE7; + cycle_count <= cycle_count + 1; + end + else begin + state <= DONE; + cycle_count <= 'd0; + xe <= 1'b0; + end + end + DONE: begin + state <= IDLE; + xe <= 1'b0; + ye <= 1'b0; + se <= 1'b0; + erase <= 1'b0; + nvstr <= 1'b0; + prog <= 1'b0; + end + endcase + +`ifdef HAS_FLASH608K + (* keep *) + FLASH608K uflash_hw0 ( + .DOUT(data_o), //output [31:0] dout + .XE(xe), //input xe + .YE(ye), //input ye + .SE(se), //input se + .PROG(prog), //input prog + .ERASE(erase), //input erase + .NVSTR(nvstr), //input nvstr + .XADR(addr[14:6]), //input [8:0] xadr + .YADR(addr[5:0]), //input [5:0] yadr + .DIN(data_i) //input [31:0] din + ); +`else + (* keep *) + FLASH64KZ uflash_hw0 ( + .DOUT(data_o), //output [31:0] dout + .XE(xe), //input xe + .YE(ye), //input ye + .SE(se), //input se + .PROG(prog), //input prog + .ERASE(erase), //input erase + .NVSTR(nvstr), //input nvstr + .XADR(addr[10:6]), //input [4:0] xadr + .YADR(addr[5:0]), //input [5:0] yadr + .DIN(data_i) //input [31:0] din + ); +`endif + +endmodule diff --git a/examples/himbaechel/userflash.v b/examples/himbaechel/userflash.v new file mode 100644 index 00000000..9ea97c2d --- /dev/null +++ b/examples/himbaechel/userflash.v @@ -0,0 +1,165 @@ +/* +* I tested performance by programming SRAM, programming in flash is left at your own risk. +* At startup, the UserFlash page is cleared, then the number 0xc001cafe is +* written, then this number is read back and displayed on the dual segment +* indicator (https://wiki.sipeed.com/hardware/en/tang/tang-PMOD/FPGA_PMOD.html#PMOD_DTx2) +* pinout: +* A - 9 A +* B - 10 F B +* C - 11 G +* D - 15 E C +* E - 16 D +* F - 17 +* G - 18 +* sel - 28 +*/ +`include "uflash_controller.v" +`default_nettype none +module top ( + input wire clk, + input wire rst_i, + input wire key_i, + output wire [2:0] led, + output wire [5:0] LCD_G +); + + wire [7:0] out; + assign led[2:0] = out[2:0]; + assign LCD_G[5:2] = out[7:3]; + + reg sel; + reg [3:0] w_strb; + wire [31:0] data_i; + wire ready; + wire [31:0] data_o; + + assign data_i = 32'hc001cafe; + + + uflash flash_ctl( + rst_i, + clk, + sel, + w_strb, + {9'h0, 6'h0}, //addr, + data_i, + ready, + data_o + ); + defparam flash_ctl.CLK_FREQ=27000000; + + reg [25:0] ctr_q; + wire [25:0] ctr_d; + + // ========= clock ============= + // Sequential code (flip-flop) + always @(posedge clk) begin + ctr_q <= ctr_d; + end + + // Combinational code (boolean logic) + assign ctr_d = ctr_q + 1'b1; + wire led_tick = ctr_q[10]; + // ============================ + + localparam ERASE = 3'h0; + localparam WAIT_ERASE_DONE = 3'h1; + localparam WRITE = 3'h2; + localparam WAIT_WRITE_DONE = 3'h3; + localparam READ = 3'h4; + localparam WAIT_READ_DONE = 3'h5; + localparam IDLE = 3'h6; + + // rotate bytes + reg [31:0] shift32; + reg [2:0] state; + always @(posedge clk or negedge rst_i) begin + if (!rst_i) begin + state <= ERASE; + sel <= 1'b1; + w_strb <= 4'b0001; + shift32 <= 32'h0; + end else begin + case (state) + ERASE: begin + state <= WAIT_ERASE_DONE; + sel <= 1'b0; + end + WAIT_ERASE_DONE: begin + if (ready) begin + state <= WRITE; + w_strb <= 4'b1111; + sel <= 1'b1; + end + end + WRITE: begin + state <= WAIT_WRITE_DONE; + sel <= 1'b0; + end + WAIT_WRITE_DONE: begin + if (ready) begin + state <= READ; + w_strb <= 4'b0000; + sel <= 1'b1; + end + end + READ: begin + state <= WAIT_READ_DONE; + sel <= 1'b0; + end + WAIT_READ_DONE: begin + if (ready) begin + state <= IDLE; + w_strb <= 4'b0000; + sel <= 1'b0; + shift32 <= data_o; + end + end + IDLE: begin + if (&ctr_q[24:0]) begin + shift32 <= {shift32[23:0], shift32[31:24]}; + end + end + endcase + end + end + + + // output + assign LCD_G[0] = led_tick; + + wire [6:0] seg[1:0]; + bin2segments left( + .halfbyte(shift32[31:28]), + .segments(seg[0]) + ); + + bin2segments right( + .halfbyte(shift32[27:24]), + .segments(seg[1]) + ); + assign out = ~seg[led_tick]; + +endmodule + +module bin2segments( + input wire [3:0] halfbyte, + output wire [6:0] segments +); + assign segments = halfbyte == 4'hf ? 7'b1110001 : + halfbyte == 4'he ? 7'b1111001 : + halfbyte == 4'hd ? 7'b1011111 : + halfbyte == 4'hc ? 7'b0111001 : + halfbyte == 4'hb ? 7'b1111100 : + halfbyte == 4'ha ? 7'b1110111 : + halfbyte == 4'h9 ? 7'b1101111 : + halfbyte == 4'h8 ? 7'b1111111 : + halfbyte == 4'h7 ? 7'b0000111 : + halfbyte == 4'h6 ? 7'b1111100 : + halfbyte == 4'h5 ? 7'b1101101 : + halfbyte == 4'h4 ? 7'b1100110 : + halfbyte == 4'h3 ? 7'b1001111 : + halfbyte == 4'h2 ? 7'b1011011 : + halfbyte == 4'h1 ? 7'b0000110 : 7'b0111111; +endmodule +