diff --git a/boards/mimas_v1/constraints.ucf b/boards/mimas_v1/constraints.ucf index 612afd7..534d841 100644 --- a/boards/mimas_v1/constraints.ucf +++ b/boards/mimas_v1/constraints.ucf @@ -2,9 +2,6 @@ NET "aclk" LOC = P126; NET "aclk" TNM_NET = "SYS_CLK_PIN"; TIMESPEC TS_SYS_CLK_PIN = PERIOD "SYS_CLK_PIN" 10 ns HIGH 50 %; -# Generated clocks -NET "clk_15" TNM_NET = "SYS_CLK_15"; -TIMESPEC TS_SYS_CLK_15 = PERIOD "SYS_CLK_15" 13.334 ns HIGH 50%; # Boards button row NET "aresetn" LOC = P120; diff --git a/project.cfg b/project.cfg index f4a228a..899910b 100644 --- a/project.cfg +++ b/project.cfg @@ -13,14 +13,14 @@ pubkey = /home/joppe/.ssh/id_rsa.pub [target.synth] toolchain = ISE ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh -# Toolchain settings family = spartan6 device = xc6slx9 package = tqg144 speedgrade = -2 toplevel = top_generic xst_opts = -vlgincdir rtl/util -files_verilog = rtl/toplevel/top_generic.v +files_verilog = rtl/util/conv.vh + rtl/toplevel/top_generic.v rtl/core/nco_q15.v rtl/core/sigmadelta_sampler.v rtl/core/sigmadelta_rcmodel_q15.v @@ -31,17 +31,17 @@ files_verilog = rtl/toplevel/top_generic.v rtl/arch/spartan-6/lvds_comparator.v rtl/arch/spartan-6/clk_gen.v files_con = boards/mimas_v1/constraints.ucf -files_other = rtl/util/conv.vh - rtl/util/rc_alpha_q15.vh +files_other = rtl/util/rc_alpha_q15.vh [target.ip] toolchain = ISE_IP +ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh family = spartan6 device = xc6slx9 package = tqg144 speedgrade = -2 -files_xco = boards/mimas_v1/ip/clk_gen.xco - +files_def = boards/mimas_v1/ip/mem_8kx8b.xco + #boards/mimas_v1/ip/clk_gen.xco [target.serv] toolchain = ISE @@ -54,8 +54,8 @@ toplevel = top_generic xst_opts = -vlgincdir rtl files_con = boards/mimas_v1/constraints.ucf files_other = sw/blinky/blinky.hex -files_verilog = rtl/util/clog2.vh - rtl/serv/serv_aligner.v + rtl/util/clog2.vh +files_verilog = rtl/serv/serv_aligner.v rtl/serv/serv_alu.v rtl/serv/serv_bufreg.v rtl/serv/serv_bufreg2.v @@ -78,8 +78,8 @@ files_verilog = rtl/util/clog2.vh rtl/serv/servile_rf_mem_if.v rtl/serv/servile.v rtl/serv/serving_ram.v - # sim/overrides/serving_ram.v rtl/serv/serving.v + rtl/arch/spartan-6/clk_gen.v rtl/wb/wb_gpio.v rtl/toplevel/top_serv.v @@ -101,4 +101,40 @@ files_verilog = sim/tb/tb_nco_q15.v sim/overrides/sigmadelta_sampler.v sim/overrides/clk_gen.v files_other = rtl/util/conv.vh - rtl/util/rc_alpha_q15.vh \ No newline at end of file + rtl/util/rc_alpha_q15.vh + +[target.servsim] +toolchain = iverilog +runtime = all +toplevel = tb_serving +ivl_opts = -Irtl/util +files_other = sw/blinky/blinky.hex + rtl/util/clog2.vh +files_verilog = rtl/serv/serv_aligner.v + rtl/serv/serv_alu.v + rtl/serv/serv_bufreg.v + rtl/serv/serv_bufreg2.v + rtl/serv/serv_compdec.v + rtl/serv/serv_csr.v + rtl/serv/serv_ctrl.v + rtl/serv/serv_debug.v + rtl/serv/serv_decode.v + rtl/serv/serv_immdec.v + rtl/serv/serv_mem_if.v + rtl/serv/serv_rf_if.v + rtl/serv/serv_rf_ram_if.v + rtl/serv/serv_rf_ram.v + rtl/serv/serv_rf_top.v + rtl/serv/serv_state.v + rtl/serv/serv_synth_wrapper.v + rtl/serv/serv_top.v + rtl/serv/servile_arbiter.v + rtl/serv/servile_mux.v + rtl/serv/servile_rf_mem_if.v + rtl/serv/servile.v + rtl/serv/serving_ram.v + rtl/serv/serving.v + rtl/arch/spartan-6/clk_gen.v + rtl/wb/wb_gpio.v + rtl/toplevel/top_serv.v + sim/tb/tb_serving.v \ No newline at end of file diff --git a/rtl/arch/spartan-6/clk_gen.v b/rtl/arch/spartan-6/clk_gen.v index d70b97f..5eee15d 100755 --- a/rtl/arch/spartan-6/clk_gen.v +++ b/rtl/arch/spartan-6/clk_gen.v @@ -67,9 +67,9 @@ (* CORE_GENERATION_INFO = "clk_gen,clk_wiz_v3_6,{component_name=clk_gen,use_phase_alignment=true,use_min_o_jitter=false,use_max_i_jitter=false,use_dyn_phase_shift=false,use_inclk_switchover=false,use_dyn_reconfig=false,feedback_source=FDBK_AUTO,primtype_sel=DCM_SP,num_out_clk=1,clkin1_period=10.0,clkin2_period=10.0,use_power_down=false,use_reset=false,use_locked=false,use_inclk_stopped=false,use_status=false,use_freeze=false,use_clk_valid=false,feedback_type=SINGLE,clock_mgr_type=AUTO,manual_override=false}" *) module clk_gen (// Clock in ports - input clk_in, + input wire clk_in, // Clock out ports - output clk_out_15 + output wire clk_out_15 ); // Input buffering @@ -78,6 +78,7 @@ module clk_gen // (.O (clkin1), // .I (clk_in)); + wire clkin1; assign clkin1 = clk_in; // Clocking primitive @@ -145,4 +146,3 @@ module clk_gen endmodule - diff --git a/rtl/serv/serv_rf_ram.v b/rtl/serv/serv_rf_ram.v index 27053b7..e5bce37 100644 --- a/rtl/serv/serv_rf_ram.v +++ b/rtl/serv/serv_rf_ram.v @@ -4,15 +4,16 @@ * SPDX-FileCopyrightText: 2019 Olof Kindgren * SPDX-License-Identifier: ISC */ +`include "../util/clog2.vh" module serv_rf_ram #(parameter width=0, parameter csr_regs=4, parameter depth=32*(32+csr_regs)/width) (input wire i_clk, - input wire [$clog2(depth)-1:0] i_waddr, + input wire [`CLOG2(depth)-1:0] i_waddr, input wire [width-1:0] i_wdata, input wire i_wen, - input wire [$clog2(depth)-1:0] i_raddr, + input wire [`CLOG2(depth)-1:0] i_raddr, input wire i_ren, output wire [width-1:0] o_rdata); @@ -28,7 +29,7 @@ module serv_rf_ram /* Reads from reg x0 needs to return 0 Check that the part of the read address corresponding to the register is zero and gate the output - width LSB of reg index $clog2(width) + width LSB of reg index `CLOG2(width) 2 4 1 4 3 2 8 2 3 @@ -38,7 +39,7 @@ module serv_rf_ram reg regzero; always @(posedge i_clk) - regzero <= !(|i_raddr[$clog2(depth)-1:5-$clog2(width)]); + regzero <= !(|i_raddr[`CLOG2(depth)-1:5-`CLOG2(width)]); assign o_rdata = rdata & ~{width{regzero}}; diff --git a/rtl/serv/serv_rf_ram_if.v b/rtl/serv/serv_rf_ram_if.v index 0ef619c..0934364 100644 --- a/rtl/serv/serv_rf_ram_if.v +++ b/rtl/serv/serv_rf_ram_if.v @@ -5,7 +5,7 @@ * SPDX-License-Identifier: ISC */ `default_nettype none -// `include "../util/clog2.vh" +`include "../util/clog2.vh" module serv_rf_ram_if #(//Data width. Adjust to preferred width of SRAM data interface parameter width=8, @@ -22,8 +22,8 @@ module serv_rf_ram_if //Internal parameters calculated from above values. Do not change parameter B=W-1, - parameter raw=clog2(32+csr_regs), //Register address width - parameter l2w=clog2(width), //log2 of width + parameter raw=`CLOG2(32+csr_regs), //Register address width + parameter l2w=`CLOG2(width), //log2 of width parameter aw=5+raw-l2w) //Address width ( //SERV side @@ -51,8 +51,8 @@ module serv_rf_ram_if input wire [width-1:0] i_rdata); localparam ratio = width/W; - localparam CMSB = 4-clog2(W); //Counter MSB - localparam l2r = clog2(ratio); + localparam CMSB = 4-`CLOG2(W); //Counter MSB + localparam l2r = `CLOG2(ratio); reg rgnt; assign o_ready = rgnt | i_wreq; diff --git a/rtl/serv/serv_rf_top.v b/rtl/serv/serv_rf_top.v index e0144ee..0d231e0 100644 --- a/rtl/serv/serv_rf_top.v +++ b/rtl/serv/serv_rf_top.v @@ -5,6 +5,7 @@ * SPDX-License-Identifier: ISC */ `default_nettype none +`include "../util/clog2.vh" module serv_rf_top #(parameter RESET_PC = 32'd0, @@ -37,7 +38,7 @@ module serv_rf_top parameter WITH_CSR = 1, parameter W = 1, parameter RF_WIDTH = W * 2, - parameter RF_L2D = $clog2((32+(WITH_CSR*4))*32/RF_WIDTH)) + parameter RF_L2D = `CLOG2((32+(WITH_CSR*4))*32/RF_WIDTH)) ( input wire clk, input wire i_rst, diff --git a/rtl/serv/serv_synth_wrapper.v b/rtl/serv/serv_synth_wrapper.v index 97991c7..50df6b7 100644 --- a/rtl/serv/serv_synth_wrapper.v +++ b/rtl/serv/serv_synth_wrapper.v @@ -5,6 +5,7 @@ * SPDX-License-Identifier: ISC */ `default_nettype none +`include "../util/clog2.vh" module serv_synth_wrapper #( @@ -22,7 +23,7 @@ module serv_synth_wrapper parameter RESET_STRATEGY = "MINI", parameter WITH_CSR = 1, parameter RF_WIDTH = 2, - parameter RF_L2D = $clog2((32+(WITH_CSR*4))*32/RF_WIDTH)) + parameter RF_L2D = `CLOG2((32+(WITH_CSR*4))*32/RF_WIDTH)) ( input wire clk, input wire i_rst, diff --git a/rtl/serv/servile.v b/rtl/serv/servile.v index 64abc4a..8e5b138 100644 --- a/rtl/serv/servile.v +++ b/rtl/serv/servile.v @@ -6,6 +6,7 @@ */ `default_nettype none +`include "../util/clog2.vh" module servile #( parameter width = 1, @@ -20,7 +21,7 @@ module servile //Internally calculated. Do not touch parameter B = width-1, parameter regs = 32+with_csr*4, - parameter rf_l2d = $clog2(regs*32/rf_width)) + parameter rf_l2d = `CLOG2(regs*32/rf_width)) ( input wire i_clk, input wire i_rst, @@ -77,14 +78,14 @@ module servile wire rf_wreq; wire rf_rreq; - wire [$clog2(regs)-1:0] wreg0; - wire [$clog2(regs)-1:0] wreg1; + wire [`CLOG2(regs)-1:0] wreg0; + wire [`CLOG2(regs)-1:0] wreg1; wire wen0; wire wen1; wire [B:0] wdata0; wire [B:0] wdata1; - wire [$clog2(regs)-1:0] rreg0; - wire [$clog2(regs)-1:0] rreg1; + wire [`CLOG2(regs)-1:0] rreg0; + wire [`CLOG2(regs)-1:0] rreg1; wire rf_ready; wire [B:0] rdata0; wire [B:0] rdata1; diff --git a/rtl/serv/servile_rf_mem_if.v b/rtl/serv/servile_rf_mem_if.v index 5b44a91..f7c613f 100644 --- a/rtl/serv/servile_rf_mem_if.v +++ b/rtl/serv/servile_rf_mem_if.v @@ -6,14 +6,15 @@ */ `default_nettype none +`include "../util/clog2.vh" module servile_rf_mem_if #(//Memory parameters parameter depth = 256, //RF parameters parameter rf_regs = 32, //Internally calculated. Do not touch - parameter rf_depth = $clog2(rf_regs*4), - parameter aw = $clog2(depth)) + parameter rf_depth = `CLOG2(rf_regs*4), + parameter aw = `CLOG2(depth)) ( input wire i_clk, input wire i_rst, diff --git a/rtl/serv/serving.v b/rtl/serv/serving.v index 1a12054..61ab779 100644 --- a/rtl/serv/serving.v +++ b/rtl/serv/serving.v @@ -18,6 +18,7 @@ */ `default_nettype none +`include "../util/clog2.vh" module serving ( input wire i_clk, @@ -56,16 +57,17 @@ module serving wire [rf_width-1:0] rf_rdata; wire rf_ren; - wire [$clog2(memsize)-1:0] sram_waddr; + wire [`CLOG2(memsize)-1:0] sram_waddr; wire [rf_width-1:0] sram_wdata; wire sram_wen; - wire [$clog2(memsize)-1:0] sram_raddr; + wire [`CLOG2(memsize)-1:0] sram_raddr; wire [rf_width-1:0] sram_rdata; wire sram_ren; serving_ram #(.memfile (memfile), - .depth (memsize)) + .depth (memsize), + .sim (sim)) ram (// Wishbone interface .i_clk (i_clk), @@ -98,7 +100,7 @@ module serving .i_sram_rdata (sram_rdata), .o_sram_ren (sram_ren), - .i_wb_adr (wb_mem_adr[$clog2(memsize)-1:2]), + .i_wb_adr (wb_mem_adr[`CLOG2(memsize)-1:2]), .i_wb_stb (wb_mem_stb), .i_wb_we (wb_mem_we) , .i_wb_sel (wb_mem_sel), diff --git a/rtl/serv/serving_ram.v b/rtl/serv/serving_ram.v index a8017ce..05429e7 100644 --- a/rtl/serv/serving_ram.v +++ b/rtl/serv/serving_ram.v @@ -18,28 +18,37 @@ */ `default_nettype none +`include "../util/clog2.vh" module serving_ram #(//Memory parameters - parameter depth = 256, - parameter aw = $clog2(depth), - parameter memfile = "") + parameter depth = 256, + parameter aw = `CLOG2(depth), + parameter memfile = "", + parameter sim = 1'b0) (input wire i_clk, - input wire [aw-1:0] i_waddr, - input wire [7:0] i_wdata, - input wire i_wen, - input wire [aw-1:0] i_raddr, - output reg [7:0] o_rdata); + input wire [aw-1:0] i_waddr, + input wire [7:0] i_wdata, + input wire i_wen, + input wire [aw-1:0] i_raddr, + output reg [7:0] o_rdata); - reg [7:0] mem [0:depth-1] /* verilator public */; + reg [7:0] mem [0:depth-1] /* verilator public */; - always @(posedge i_clk) begin - if (i_wen) mem[i_waddr] <= i_wdata; - o_rdata <= mem[i_raddr]; + always @(posedge i_clk) begin + if (i_wen) mem[i_waddr] <= i_wdata; + o_rdata <= mem[i_raddr]; + end + + integer i; + initial begin + if(sim==1'b1) begin + for (i = 0; i < depth; i = i + 1) + mem[i] = 8'h00; + + end + if(|memfile) begin + $display("Preloading %m from %s", memfile); + $readmemh(memfile, mem); + end end - - initial - if(|memfile) begin - $display("Preloading %m from %s", memfile); - $readmemh(memfile, mem); - end endmodule diff --git a/rtl/toplevel/top_generic.v b/rtl/toplevel/top_generic.v index c33ef9b..3a576a9 100644 --- a/rtl/toplevel/top_generic.v +++ b/rtl/toplevel/top_generic.v @@ -26,7 +26,7 @@ module top_generic( localparam integer DIV_MAX = 100_000 - 1; // 1 ms tick at 100 MHz reg [16:0] div_counter = 0; // enough bits for 100k (2^17=131072) reg [31:0] freq; - always @(posedge aclk) begin + always @(posedge clk_15) begin if (!aresetn) begin div_counter <= 0; count <= 0; @@ -48,10 +48,10 @@ module top_generic( wire [15:0] sin_q15; wire clk_en; nco_q15 #( - .CLK_HZ(100_000_000), + .CLK_HZ(15_000_000), .FS_HZ(40_000) ) nco ( - .clk (aclk), + .clk (clk_15), .rst_n (aresetn), .freq_hz(freq), .sin_q15(sin_q15), @@ -60,7 +60,7 @@ module top_generic( ); reg [5:0] dac_code; - always @(posedge aclk) begin + always @(posedge clk_15) begin dac_code <= q15_to_uq16(sin_q15) >> 10; end assign r2r = dac_code; diff --git a/rtl/toplevel/top_serv.v b/rtl/toplevel/top_serv.v index 1addbc5..54143ae 100644 --- a/rtl/toplevel/top_serv.v +++ b/rtl/toplevel/top_serv.v @@ -10,6 +10,15 @@ module top_generic( output wire[5:0] r2r ); + // Clocking + wire clk_100; + wire clk_15; + assign clk_100 = aclk; + clk_gen clocking( + .clk_in(clk_100), + .clk_out_15(clk_15) + ); + wire [31:0] wb_adr; wire [31:0] wb_dat; wire [31:0] wb_rdt; @@ -31,7 +40,7 @@ module top_generic( .RESET_STRATEGY("MINI"), .WITH_CSR(1) ) serv ( - .i_clk(aclk), + .i_clk(clk_15), .i_rst(!aresetn), .i_timer_irq(1'b0), .i_wb_rdt(wb_rdt), @@ -44,9 +53,9 @@ module top_generic( ); wb_gpio #( - .address(32'h80000000) + .address(32'h40000000) ) gpio ( - .i_wb_clk(aclk), + .i_wb_clk(clk_15), .i_wb_rst(!aresetn), .i_wb_dat(wb_dat), .i_wb_adr(wb_adr), diff --git a/rtl/util/clog2.vh b/rtl/util/clog2.vh index 4fa2ac1..0aad108 100644 --- a/rtl/util/clog2.vh +++ b/rtl/util/clog2.vh @@ -1,10 +1,39 @@ -function integer clog2; - input integer value; - integer i; - begin - value = value - 1; - for (i = 0; value > 0; i = i + 1) - value = value >> 1; - clog2 = (i < 1) ? 1 : i; - end -endfunction \ No newline at end of file +`ifndef CLOG2_VH +`define CLOG2_VH + +// Verilog-2001 compatible ceil(log2(x)) macro (matches $clog2 semantics). +`define CLOG2(x) \ + (((x) <= 1) ? 0 : \ + ((x) <= 2) ? 1 : \ + ((x) <= 4) ? 2 : \ + ((x) <= 8) ? 3 : \ + ((x) <= 16) ? 4 : \ + ((x) <= 32) ? 5 : \ + ((x) <= 64) ? 6 : \ + ((x) <= 128) ? 7 : \ + ((x) <= 256) ? 8 : \ + ((x) <= 512) ? 9 : \ + ((x) <= 1024) ? 10 : \ + ((x) <= 2048) ? 11 : \ + ((x) <= 4096) ? 12 : \ + ((x) <= 8192) ? 13 : \ + ((x) <= 16384) ? 14 : \ + ((x) <= 32768) ? 15 : \ + ((x) <= 65536) ? 16 : \ + ((x) <= 131072) ? 17 : \ + ((x) <= 262144) ? 18 : \ + ((x) <= 524288) ? 19 : \ + ((x) <= 1048576) ? 20 : \ + ((x) <= 2097152) ? 21 : \ + ((x) <= 4194304) ? 22 : \ + ((x) <= 8388608) ? 23 : \ + ((x) <= 16777216) ? 24 : \ + ((x) <= 33554432) ? 25 : \ + ((x) <= 67108864) ? 26 : \ + ((x) <= 134217728) ? 27 : \ + ((x) <= 268435456) ? 28 : \ + ((x) <= 536870912) ? 29 : \ + ((x) <= 1073741824) ? 30 : \ + ((x) <= 2147483648) ? 31 : 32) + +`endif diff --git a/scripts/hex_to_coe.py b/scripts/hex_to_coe.py new file mode 100755 index 0000000..a6c4e63 --- /dev/null +++ b/scripts/hex_to_coe.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +"""Convert a simple .hex image to Xilinx .coe format. + +Supported input: +- One or more hex tokens per line (e.g. "37" or "0x37") +- Optional comments after '#' or '//' + +By default, each token is written as one .coe entry (8-bit style memory init). +Use --word-bytes > 1 to pack byte tokens into wider words. +""" + +from __future__ import annotations + +import argparse +from pathlib import Path + + +def parse_tokens(path: Path) -> list[int]: + values: list[int] = [] + for raw in path.read_text().splitlines(): + line = raw.split("//", 1)[0].split("#", 1)[0].strip() + if not line: + continue + for token in line.replace(",", " ").split(): + token = token.strip() + if not token: + continue + if token.lower().startswith("0x"): + token = token[2:] + values.append(int(token, 16)) + return values + + +def pack_words( + byte_values: list[int], word_bytes: int, little_endian: bool +) -> tuple[list[int], int]: + if word_bytes <= 0: + raise ValueError("word_bytes must be >= 1") + if word_bytes == 1: + return byte_values[:], 2 + + words: list[int] = [] + width = word_bytes * 2 + for i in range(0, len(byte_values), word_bytes): + chunk = byte_values[i : i + word_bytes] + if len(chunk) < word_bytes: + chunk = chunk + [0] * (word_bytes - len(chunk)) + + word = 0 + if little_endian: + for b_idx, b in enumerate(chunk): + word |= (b & 0xFF) << (8 * b_idx) + else: + for b in chunk: + word = (word << 8) | (b & 0xFF) + words.append(word) + + return words, width + + +def main() -> None: + parser = argparse.ArgumentParser(description="Convert .hex to Xilinx .coe") + parser.add_argument("input_hex", type=Path, help="Input .hex file") + parser.add_argument("output_coe", type=Path, help="Output .coe file") + parser.add_argument( + "--word-bytes", + type=int, + default=1, + help="Bytes per output word (default: 1)", + ) + parser.add_argument( + "--little-endian", + action="store_true", + help="Pack bytes little-endian when --word-bytes > 1 (default)", + ) + parser.add_argument( + "--big-endian", + action="store_true", + help="Pack bytes big-endian when --word-bytes > 1", + ) + parser.add_argument( + "--radix", + type=int, + default=16, + choices=[2, 10, 16], + help="COE radix (default: 16)", + ) + parser.add_argument( + "--depth", + type=int, + default=0, + help="Optional output depth (pads with zeros up to this many words)", + ) + args = parser.parse_args() + + if args.little_endian and args.big_endian: + raise SystemExit("Choose only one of --little-endian or --big-endian") + + little_endian = True + if args.big_endian: + little_endian = False + + byte_values = parse_tokens(args.input_hex) + words, hex_digits = pack_words(byte_values, args.word_bytes, little_endian) + + if args.depth > 0 and args.depth < len(words): + raise SystemExit( + f"Requested --depth={args.depth} but image has {len(words)} words" + ) + if args.depth > len(words): + words.extend([0] * (args.depth - len(words))) + + if args.radix == 16: + data = [f"{w:0{hex_digits}X}" for w in words] + elif args.radix == 10: + data = [str(w) for w in words] + else: + width_bits = args.word_bytes * 8 + data = [f"{w:0{width_bits}b}" for w in words] + + out_lines = [ + f"memory_initialization_radix={args.radix};", + "memory_initialization_vector=", + ] + if data: + out_lines.extend( + [f"{v}," for v in data[:-1]] + [f"{data[-1]};"] + ) + else: + out_lines.append("0;") + + args.output_coe.write_text("\n".join(out_lines) + "\n") + + +if __name__ == "__main__": + main() diff --git a/scripts/hex_to_mif.py b/scripts/hex_to_mif.py new file mode 100755 index 0000000..bed01bb --- /dev/null +++ b/scripts/hex_to_mif.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Convert a simple .hex image to a plain .mif-style binary file. + +Default output format matches build/mem_8kx8b.mif in this repo: +- one binary word per line +- no header +""" + +from __future__ import annotations + +import argparse +from pathlib import Path + + +def parse_tokens(path: Path) -> list[int]: + values: list[int] = [] + for raw in path.read_text().splitlines(): + line = raw.split("//", 1)[0].split("#", 1)[0].strip() + if not line: + continue + for token in line.replace(",", " ").split(): + if token.lower().startswith("0x"): + token = token[2:] + values.append(int(token, 16)) + return values + + +def pack_words( + byte_values: list[int], word_bytes: int, little_endian: bool +) -> list[int]: + if word_bytes <= 0: + raise ValueError("word_bytes must be >= 1") + if word_bytes == 1: + return byte_values[:] + + words: list[int] = [] + for i in range(0, len(byte_values), word_bytes): + chunk = byte_values[i : i + word_bytes] + if len(chunk) < word_bytes: + chunk = chunk + [0] * (word_bytes - len(chunk)) + + word = 0 + if little_endian: + for b_idx, b in enumerate(chunk): + word |= (b & 0xFF) << (8 * b_idx) + else: + for b in chunk: + word = (word << 8) | (b & 0xFF) + words.append(word) + return words + + +def main() -> None: + parser = argparse.ArgumentParser(description="Convert .hex to plain .mif") + parser.add_argument("input_hex", type=Path, help="Input .hex file") + parser.add_argument("output_mif", type=Path, help="Output .mif file") + parser.add_argument( + "--word-bytes", + type=int, + default=1, + help="Bytes per output word (default: 1)", + ) + parser.add_argument( + "--little-endian", + action="store_true", + help="Pack bytes little-endian when --word-bytes > 1 (default)", + ) + parser.add_argument( + "--big-endian", + action="store_true", + help="Pack bytes big-endian when --word-bytes > 1", + ) + parser.add_argument( + "--depth", + type=int, + default=0, + help="Optional output depth (pads with zeros up to this many words)", + ) + args = parser.parse_args() + + if args.little_endian and args.big_endian: + raise SystemExit("Choose only one of --little-endian or --big-endian") + + little_endian = True + if args.big_endian: + little_endian = False + + words = pack_words(parse_tokens(args.input_hex), args.word_bytes, little_endian) + width_bits = args.word_bytes * 8 + max_word = (1 << width_bits) - 1 + + if args.depth > 0 and args.depth < len(words): + raise SystemExit( + f"Requested --depth={args.depth} but image has {len(words)} words" + ) + if args.depth > len(words): + words.extend([0] * (args.depth - len(words))) + + lines = [f"{(w & max_word):0{width_bits}b}" for w in words] + args.output_mif.write_text("\n".join(lines) + ("\n" if lines else "")) + + +if __name__ == "__main__": + main() diff --git a/sim/overrides/serving_ram.v b/sim/overrides/serving_ram.v deleted file mode 100644 index 4516baa..0000000 --- a/sim/overrides/serving_ram.v +++ /dev/null @@ -1,51 +0,0 @@ -/* serving_ram.v : I/D SRAM for the serving SoC - * - * ISC License - * - * Copyright (C) 2020 Olof Kindgren - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -`default_nettype none -module serving_ram - #(//Memory parameters - parameter depth = 256, - parameter aw = $clog2(depth), - parameter memfile = "") - (input wire i_clk, - input wire [aw-1:0] i_waddr, - input wire [7:0] i_wdata, - input wire i_wen, - input wire [aw-1:0] i_raddr, - output reg [7:0] o_rdata); - - reg [7:0] mem [0:depth-1] /* verilator public */; - - always @(posedge i_clk) begin - if (i_wen) mem[i_waddr] <= i_wdata; - o_rdata <= mem[i_raddr]; - end - - integer i; -initial begin - // Fill unused/uninitialized memory with a predefined value - for (i = 0; i < depth; i = i + 1) - mem[i] = 8'h00; // <- pick your fill value (00, FF, etc.) - - if (|memfile) begin - $display("Preloading %m from %s", memfile); - $readmemh(memfile, mem); - end -end -endmodule diff --git a/sim/tb/tb_serving.v b/sim/tb/tb_serving.v index 70e1c16..f44f6c6 100644 --- a/sim/tb/tb_serving.v +++ b/sim/tb/tb_serving.v @@ -33,7 +33,7 @@ module tb_serving(); serving #( .memfile("../sw/blinky/blinky.hex"), .memsize(8192), - .sim(1'b0), + .sim(1'b1), .RESET_STRATEGY("MINI"), .WITH_CSR(1) ) serv ( @@ -50,7 +50,7 @@ module tb_serving(); ); wb_gpio #( - .address(32'h80000000) + .address(32'h40000000) ) gpio ( .i_wb_clk(clk), .i_wb_dat(wb_dat), diff --git a/sw/.gitignore b/sw/.gitignore new file mode 100644 index 0000000..033d388 --- /dev/null +++ b/sw/.gitignore @@ -0,0 +1,8 @@ +*.o +*.hex +*.bin +*.map +*.elf.asm +*.elf +*.coe +*.mif \ No newline at end of file diff --git a/sw/blinky/Makefile b/sw/blinky/Makefile index 9684305..80c1417 100644 --- a/sw/blinky/Makefile +++ b/sw/blinky/Makefile @@ -1,14 +1,56 @@ -TOOLCHAIN_PREFIX?=riscv64-elf- -CC=$(TOOLCHAIN_PREFIX)gcc -OBJCOPY=$(TOOLCHAIN_PREFIX)objcopy +TOOLCHAIN_PREFIX ?= riscv64-elf- -%.elf: %.S link.ld -# $(CC) -nostartfiles -nostdlib -march=rv32i_zicsr -mabi=ilp32 -Tlink.ld -o$@ $< - $(CC) -nostartfiles -nostdlib -ffreestanding -march=rv32i_zicsr -mabi=ilp32 -Tlink.ld -o$@ $< -%.bin: %.elf +CC := $(TOOLCHAIN_PREFIX)gcc +OBJCOPY := $(TOOLCHAIN_PREFIX)objcopy +OBJDUMP := $(TOOLCHAIN_PREFIX)objdump +SIZE := $(TOOLCHAIN_PREFIX)size + +TARGET := blinky +SRCS_C := blinky.c +SRCS_S := start.s +OBJS := $(SRCS_C:.c=.o) $(SRCS_S:.s=.o) + +ARCH_FLAGS := -march=rv32i_zicsr -mabi=ilp32 +CFLAGS := $(ARCH_FLAGS) -Os -ffreestanding -fno-builtin -Wall -Wextra +ASFLAGS := $(ARCH_FLAGS) +LDFLAGS := $(ARCH_FLAGS) -nostdlib -nostartfiles -Wl,-Bstatic,-Tlink.ld,--gc-sections,-Map,$(TARGET).map + +HEX_TO_COE := ../../scripts/hex_to_coe.py +HEX_TO_MIF := ../../scripts/hex_to_mif.py + +.PHONY: all clean disasm size + +all: $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).coe $(TARGET).mif $(TARGET).elf.asm + +$(TARGET).elf: $(OBJS) link.ld + $(CC) $(LDFLAGS) -o $@ $(OBJS) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.o: %.s + $(CC) $(ASFLAGS) -c -o $@ $< + +$(TARGET).bin: $(TARGET).elf $(OBJCOPY) -O binary $< $@ -%.hex: %.bin + +$(TARGET).hex: $(TARGET).bin hexdump -v -e '1/1 "%02x\n"' $< > $@ +$(TARGET).coe: $(TARGET).hex + $(HEX_TO_COE) $< $@ + +$(TARGET).mif: $(TARGET).hex + $(HEX_TO_MIF) $< $@ + +$(TARGET).elf.asm: $(TARGET).elf + $(OBJDUMP) -d -S $< > $@ + +disasm: $(TARGET).elf.asm + +size: $(TARGET).elf + $(SIZE) $< + clean: - rm -f *.elf *.bin *.hex + rm -f $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).coe $(TARGET).mif \ + $(TARGET).elf.asm $(TARGET).map $(OBJS) diff --git a/sw/blinky/blinky.S b/sw/blinky/blinky.S deleted file mode 100644 index 2cc3e44..0000000 --- a/sw/blinky/blinky.S +++ /dev/null @@ -1,17 +0,0 @@ -#define GPIO_BASE 0x80000000 -#define DELAY 2 - -.globl _start -_start: - lui a0, %hi(GPIO_BASE) - addi a0, a0, %lo(GPIO_BASE) - addi t0, zero, 0 - li t1, DELAY -.lp1: - sb t0, 0(a0) - addi t0, t0, 1 - and t2, zero, zero -time1: - addi t2, t2, 1 - bne t1, t2, time1 - j .lp1 diff --git a/sw/blinky/blinky.c b/sw/blinky/blinky.c new file mode 100644 index 0000000..fdf9dfe --- /dev/null +++ b/sw/blinky/blinky.c @@ -0,0 +1,22 @@ +#include + +#define GPIO_BASE 0x40000000u + +static volatile uint32_t * const gpio = (volatile uint32_t *)GPIO_BASE; + +static void delay(volatile uint32_t ticks){ + while (ticks--) { + __asm__ volatile ("nop"); + } +} + +int main(void) +{ + uint32_t v = 0; + + for (;;) { + *gpio = v; + v++; + delay(20000u); + } +} diff --git a/sw/blinky/link.ld b/sw/blinky/link.ld index 3398348..b14568d 100644 --- a/sw/blinky/link.ld +++ b/sw/blinky/link.ld @@ -1,10 +1,33 @@ -OUTPUT_ARCH( "riscv" ) +OUTPUT_ARCH("riscv") ENTRY(_start) +MEMORY +{ + RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8K +} + SECTIONS { - . = 0x00000000; - .text : { *(.text) } - .data : { *(.data) } - .bss : { *(.bss) } + .text : + { + KEEP(*(.text.init)) + *(.text .text.*) + *(.rodata .rodata.*) + } > RAM + + .data : + { + *(.data .data.*) + } > RAM + + .bss (NOLOAD) : + { + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } > RAM + + . = ALIGN(4); + __stack_top = ORIGIN(RAM) + LENGTH(RAM); } diff --git a/sw/blinky/start.s b/sw/blinky/start.s new file mode 100644 index 0000000..837498e --- /dev/null +++ b/sw/blinky/start.s @@ -0,0 +1,23 @@ +.section .text.init +.globl _start +.type _start, @function + +_start: + la sp, __stack_top + + # Zero .bss + la t0, __bss_start + la t1, __bss_end +1: + bgeu t0, t1, 2f + sw zero, 0(t0) + addi t0, t0, 4 + j 1b + +2: + call main + +3: + j 3b + +.size _start, .-_start