Working SERV cpu

This commit is contained in:
2026-02-22 18:48:17 +01:00
parent ac6aea90b6
commit 5e951f9b61
24 changed files with 534 additions and 157 deletions

View File

@@ -2,9 +2,6 @@
NET "aclk" LOC = P126; NET "aclk" LOC = P126;
NET "aclk" TNM_NET = "SYS_CLK_PIN"; NET "aclk" TNM_NET = "SYS_CLK_PIN";
TIMESPEC TS_SYS_CLK_PIN = PERIOD "SYS_CLK_PIN" 10 ns HIGH 50 %; 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 # Boards button row
NET "aresetn" LOC = P120; NET "aresetn" LOC = P120;

View File

@@ -13,14 +13,14 @@ pubkey = /home/joppe/.ssh/id_rsa.pub
[target.synth] [target.synth]
toolchain = ISE toolchain = ISE
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
# Toolchain settings
family = spartan6 family = spartan6
device = xc6slx9 device = xc6slx9
package = tqg144 package = tqg144
speedgrade = -2 speedgrade = -2
toplevel = top_generic toplevel = top_generic
xst_opts = -vlgincdir rtl/util 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/nco_q15.v
rtl/core/sigmadelta_sampler.v rtl/core/sigmadelta_sampler.v
rtl/core/sigmadelta_rcmodel_q15.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/lvds_comparator.v
rtl/arch/spartan-6/clk_gen.v rtl/arch/spartan-6/clk_gen.v
files_con = boards/mimas_v1/constraints.ucf files_con = boards/mimas_v1/constraints.ucf
files_other = rtl/util/conv.vh files_other = rtl/util/rc_alpha_q15.vh
rtl/util/rc_alpha_q15.vh
[target.ip] [target.ip]
toolchain = ISE_IP toolchain = ISE_IP
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
family = spartan6 family = spartan6
device = xc6slx9 device = xc6slx9
package = tqg144 package = tqg144
speedgrade = -2 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] [target.serv]
toolchain = ISE toolchain = ISE
@@ -54,8 +54,8 @@ toplevel = top_generic
xst_opts = -vlgincdir rtl xst_opts = -vlgincdir rtl
files_con = boards/mimas_v1/constraints.ucf files_con = boards/mimas_v1/constraints.ucf
files_other = sw/blinky/blinky.hex files_other = sw/blinky/blinky.hex
files_verilog = rtl/util/clog2.vh rtl/util/clog2.vh
rtl/serv/serv_aligner.v files_verilog = rtl/serv/serv_aligner.v
rtl/serv/serv_alu.v rtl/serv/serv_alu.v
rtl/serv/serv_bufreg.v rtl/serv/serv_bufreg.v
rtl/serv/serv_bufreg2.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_rf_mem_if.v
rtl/serv/servile.v rtl/serv/servile.v
rtl/serv/serving_ram.v rtl/serv/serving_ram.v
# sim/overrides/serving_ram.v
rtl/serv/serving.v rtl/serv/serving.v
rtl/arch/spartan-6/clk_gen.v
rtl/wb/wb_gpio.v rtl/wb/wb_gpio.v
rtl/toplevel/top_serv.v rtl/toplevel/top_serv.v
@@ -102,3 +102,39 @@ files_verilog = sim/tb/tb_nco_q15.v
sim/overrides/clk_gen.v sim/overrides/clk_gen.v
files_other = rtl/util/conv.vh files_other = rtl/util/conv.vh
rtl/util/rc_alpha_q15.vh 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

View File

@@ -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}" *) (* 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 module clk_gen
(// Clock in ports (// Clock in ports
input clk_in, input wire clk_in,
// Clock out ports // Clock out ports
output clk_out_15 output wire clk_out_15
); );
// Input buffering // Input buffering
@@ -78,6 +78,7 @@ module clk_gen
// (.O (clkin1), // (.O (clkin1),
// .I (clk_in)); // .I (clk_in));
wire clkin1;
assign clkin1 = clk_in; assign clkin1 = clk_in;
// Clocking primitive // Clocking primitive
@@ -145,4 +146,3 @@ module clk_gen
endmodule endmodule

View File

@@ -4,15 +4,16 @@
* SPDX-FileCopyrightText: 2019 Olof Kindgren <olof@award-winning.me> * SPDX-FileCopyrightText: 2019 Olof Kindgren <olof@award-winning.me>
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
*/ */
`include "../util/clog2.vh"
module serv_rf_ram module serv_rf_ram
#(parameter width=0, #(parameter width=0,
parameter csr_regs=4, parameter csr_regs=4,
parameter depth=32*(32+csr_regs)/width) parameter depth=32*(32+csr_regs)/width)
(input wire i_clk, (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 [width-1:0] i_wdata,
input wire i_wen, input wire i_wen,
input wire [$clog2(depth)-1:0] i_raddr, input wire [`CLOG2(depth)-1:0] i_raddr,
input wire i_ren, input wire i_ren,
output wire [width-1:0] o_rdata); output wire [width-1:0] o_rdata);
@@ -28,7 +29,7 @@ module serv_rf_ram
/* Reads from reg x0 needs to return 0 /* Reads from reg x0 needs to return 0
Check that the part of the read address corresponding to the register Check that the part of the read address corresponding to the register
is zero and gate the output is zero and gate the output
width LSB of reg index $clog2(width) width LSB of reg index `CLOG2(width)
2 4 1 2 4 1
4 3 2 4 3 2
8 2 3 8 2 3
@@ -38,7 +39,7 @@ module serv_rf_ram
reg regzero; reg regzero;
always @(posedge i_clk) 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}}; assign o_rdata = rdata & ~{width{regzero}};

View File

@@ -5,7 +5,7 @@
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
*/ */
`default_nettype none `default_nettype none
// `include "../util/clog2.vh" `include "../util/clog2.vh"
module serv_rf_ram_if module serv_rf_ram_if
#(//Data width. Adjust to preferred width of SRAM data interface #(//Data width. Adjust to preferred width of SRAM data interface
parameter width=8, parameter width=8,
@@ -22,8 +22,8 @@ module serv_rf_ram_if
//Internal parameters calculated from above values. Do not change //Internal parameters calculated from above values. Do not change
parameter B=W-1, parameter B=W-1,
parameter raw=clog2(32+csr_regs), //Register address width parameter raw=`CLOG2(32+csr_regs), //Register address width
parameter l2w=clog2(width), //log2 of width parameter l2w=`CLOG2(width), //log2 of width
parameter aw=5+raw-l2w) //Address width parameter aw=5+raw-l2w) //Address width
( (
//SERV side //SERV side
@@ -51,8 +51,8 @@ module serv_rf_ram_if
input wire [width-1:0] i_rdata); input wire [width-1:0] i_rdata);
localparam ratio = width/W; localparam ratio = width/W;
localparam CMSB = 4-clog2(W); //Counter MSB localparam CMSB = 4-`CLOG2(W); //Counter MSB
localparam l2r = clog2(ratio); localparam l2r = `CLOG2(ratio);
reg rgnt; reg rgnt;
assign o_ready = rgnt | i_wreq; assign o_ready = rgnt | i_wreq;

View File

@@ -5,6 +5,7 @@
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
*/ */
`default_nettype none `default_nettype none
`include "../util/clog2.vh"
module serv_rf_top module serv_rf_top
#(parameter RESET_PC = 32'd0, #(parameter RESET_PC = 32'd0,
@@ -37,7 +38,7 @@ module serv_rf_top
parameter WITH_CSR = 1, parameter WITH_CSR = 1,
parameter W = 1, parameter W = 1,
parameter RF_WIDTH = W * 2, 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 clk,
input wire i_rst, input wire i_rst,

View File

@@ -5,6 +5,7 @@
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
*/ */
`default_nettype none `default_nettype none
`include "../util/clog2.vh"
module serv_synth_wrapper module serv_synth_wrapper
#( #(
@@ -22,7 +23,7 @@ module serv_synth_wrapper
parameter RESET_STRATEGY = "MINI", parameter RESET_STRATEGY = "MINI",
parameter WITH_CSR = 1, parameter WITH_CSR = 1,
parameter RF_WIDTH = 2, 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 clk,
input wire i_rst, input wire i_rst,

View File

@@ -6,6 +6,7 @@
*/ */
`default_nettype none `default_nettype none
`include "../util/clog2.vh"
module servile module servile
#( #(
parameter width = 1, parameter width = 1,
@@ -20,7 +21,7 @@ module servile
//Internally calculated. Do not touch //Internally calculated. Do not touch
parameter B = width-1, parameter B = width-1,
parameter regs = 32+with_csr*4, 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_clk,
input wire i_rst, input wire i_rst,
@@ -77,14 +78,14 @@ module servile
wire rf_wreq; wire rf_wreq;
wire rf_rreq; wire rf_rreq;
wire [$clog2(regs)-1:0] wreg0; wire [`CLOG2(regs)-1:0] wreg0;
wire [$clog2(regs)-1:0] wreg1; wire [`CLOG2(regs)-1:0] wreg1;
wire wen0; wire wen0;
wire wen1; wire wen1;
wire [B:0] wdata0; wire [B:0] wdata0;
wire [B:0] wdata1; wire [B:0] wdata1;
wire [$clog2(regs)-1:0] rreg0; wire [`CLOG2(regs)-1:0] rreg0;
wire [$clog2(regs)-1:0] rreg1; wire [`CLOG2(regs)-1:0] rreg1;
wire rf_ready; wire rf_ready;
wire [B:0] rdata0; wire [B:0] rdata0;
wire [B:0] rdata1; wire [B:0] rdata1;

View File

@@ -6,14 +6,15 @@
*/ */
`default_nettype none `default_nettype none
`include "../util/clog2.vh"
module servile_rf_mem_if module servile_rf_mem_if
#(//Memory parameters #(//Memory parameters
parameter depth = 256, parameter depth = 256,
//RF parameters //RF parameters
parameter rf_regs = 32, parameter rf_regs = 32,
//Internally calculated. Do not touch //Internally calculated. Do not touch
parameter rf_depth = $clog2(rf_regs*4), parameter rf_depth = `CLOG2(rf_regs*4),
parameter aw = $clog2(depth)) parameter aw = `CLOG2(depth))
( (
input wire i_clk, input wire i_clk,
input wire i_rst, input wire i_rst,

View File

@@ -18,6 +18,7 @@
*/ */
`default_nettype none `default_nettype none
`include "../util/clog2.vh"
module serving module serving
( (
input wire i_clk, input wire i_clk,
@@ -56,16 +57,17 @@ module serving
wire [rf_width-1:0] rf_rdata; wire [rf_width-1:0] rf_rdata;
wire rf_ren; 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 [rf_width-1:0] sram_wdata;
wire sram_wen; 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 [rf_width-1:0] sram_rdata;
wire sram_ren; wire sram_ren;
serving_ram serving_ram
#(.memfile (memfile), #(.memfile (memfile),
.depth (memsize)) .depth (memsize),
.sim (sim))
ram ram
(// Wishbone interface (// Wishbone interface
.i_clk (i_clk), .i_clk (i_clk),
@@ -98,7 +100,7 @@ module serving
.i_sram_rdata (sram_rdata), .i_sram_rdata (sram_rdata),
.o_sram_ren (sram_ren), .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_stb (wb_mem_stb),
.i_wb_we (wb_mem_we) , .i_wb_we (wb_mem_we) ,
.i_wb_sel (wb_mem_sel), .i_wb_sel (wb_mem_sel),

View File

@@ -18,11 +18,13 @@
*/ */
`default_nettype none `default_nettype none
`include "../util/clog2.vh"
module serving_ram module serving_ram
#(//Memory parameters #(//Memory parameters
parameter depth = 256, parameter depth = 256,
parameter aw = $clog2(depth), parameter aw = `CLOG2(depth),
parameter memfile = "") parameter memfile = "",
parameter sim = 1'b0)
(input wire i_clk, (input wire i_clk,
input wire [aw-1:0] i_waddr, input wire [aw-1:0] i_waddr,
input wire [7:0] i_wdata, input wire [7:0] i_wdata,
@@ -37,9 +39,16 @@ module serving_ram
o_rdata <= mem[i_raddr]; o_rdata <= mem[i_raddr];
end end
initial 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 if(|memfile) begin
$display("Preloading %m from %s", memfile); $display("Preloading %m from %s", memfile);
$readmemh(memfile, mem); $readmemh(memfile, mem);
end end
end
endmodule endmodule

View File

@@ -26,7 +26,7 @@ module top_generic(
localparam integer DIV_MAX = 100_000 - 1; // 1 ms tick at 100 MHz 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 [16:0] div_counter = 0; // enough bits for 100k (2^17=131072)
reg [31:0] freq; reg [31:0] freq;
always @(posedge aclk) begin always @(posedge clk_15) begin
if (!aresetn) begin if (!aresetn) begin
div_counter <= 0; div_counter <= 0;
count <= 0; count <= 0;
@@ -48,10 +48,10 @@ module top_generic(
wire [15:0] sin_q15; wire [15:0] sin_q15;
wire clk_en; wire clk_en;
nco_q15 #( nco_q15 #(
.CLK_HZ(100_000_000), .CLK_HZ(15_000_000),
.FS_HZ(40_000) .FS_HZ(40_000)
) nco ( ) nco (
.clk (aclk), .clk (clk_15),
.rst_n (aresetn), .rst_n (aresetn),
.freq_hz(freq), .freq_hz(freq),
.sin_q15(sin_q15), .sin_q15(sin_q15),
@@ -60,7 +60,7 @@ module top_generic(
); );
reg [5:0] dac_code; reg [5:0] dac_code;
always @(posedge aclk) begin always @(posedge clk_15) begin
dac_code <= q15_to_uq16(sin_q15) >> 10; dac_code <= q15_to_uq16(sin_q15) >> 10;
end end
assign r2r = dac_code; assign r2r = dac_code;

View File

@@ -10,6 +10,15 @@ module top_generic(
output wire[5:0] r2r 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_adr;
wire [31:0] wb_dat; wire [31:0] wb_dat;
wire [31:0] wb_rdt; wire [31:0] wb_rdt;
@@ -31,7 +40,7 @@ module top_generic(
.RESET_STRATEGY("MINI"), .RESET_STRATEGY("MINI"),
.WITH_CSR(1) .WITH_CSR(1)
) serv ( ) serv (
.i_clk(aclk), .i_clk(clk_15),
.i_rst(!aresetn), .i_rst(!aresetn),
.i_timer_irq(1'b0), .i_timer_irq(1'b0),
.i_wb_rdt(wb_rdt), .i_wb_rdt(wb_rdt),
@@ -44,9 +53,9 @@ module top_generic(
); );
wb_gpio #( wb_gpio #(
.address(32'h80000000) .address(32'h40000000)
) gpio ( ) gpio (
.i_wb_clk(aclk), .i_wb_clk(clk_15),
.i_wb_rst(!aresetn), .i_wb_rst(!aresetn),
.i_wb_dat(wb_dat), .i_wb_dat(wb_dat),
.i_wb_adr(wb_adr), .i_wb_adr(wb_adr),

View File

@@ -1,10 +1,39 @@
function integer clog2; `ifndef CLOG2_VH
input integer value; `define CLOG2_VH
integer i;
begin // Verilog-2001 compatible ceil(log2(x)) macro (matches $clog2 semantics).
value = value - 1; `define CLOG2(x) \
for (i = 0; value > 0; i = i + 1) (((x) <= 1) ? 0 : \
value = value >> 1; ((x) <= 2) ? 1 : \
clog2 = (i < 1) ? 1 : i; ((x) <= 4) ? 2 : \
end ((x) <= 8) ? 3 : \
endfunction ((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

136
scripts/hex_to_coe.py Executable file
View File

@@ -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()

104
scripts/hex_to_mif.py Executable file
View File

@@ -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()

View File

@@ -1,51 +0,0 @@
/* serving_ram.v : I/D SRAM for the serving SoC
*
* ISC License
*
* Copyright (C) 2020 Olof Kindgren <olof.kindgren@gmail.com>
*
* 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

View File

@@ -33,7 +33,7 @@ module tb_serving();
serving #( serving #(
.memfile("../sw/blinky/blinky.hex"), .memfile("../sw/blinky/blinky.hex"),
.memsize(8192), .memsize(8192),
.sim(1'b0), .sim(1'b1),
.RESET_STRATEGY("MINI"), .RESET_STRATEGY("MINI"),
.WITH_CSR(1) .WITH_CSR(1)
) serv ( ) serv (
@@ -50,7 +50,7 @@ module tb_serving();
); );
wb_gpio #( wb_gpio #(
.address(32'h80000000) .address(32'h40000000)
) gpio ( ) gpio (
.i_wb_clk(clk), .i_wb_clk(clk),
.i_wb_dat(wb_dat), .i_wb_dat(wb_dat),

8
sw/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
*.o
*.hex
*.bin
*.map
*.elf.asm
*.elf
*.coe
*.mif

View File

@@ -1,14 +1,56 @@
TOOLCHAIN_PREFIX ?= riscv64-elf- TOOLCHAIN_PREFIX ?= riscv64-elf-
CC=$(TOOLCHAIN_PREFIX)gcc
OBJCOPY=$(TOOLCHAIN_PREFIX)objcopy
%.elf: %.S link.ld CC := $(TOOLCHAIN_PREFIX)gcc
# $(CC) -nostartfiles -nostdlib -march=rv32i_zicsr -mabi=ilp32 -Tlink.ld -o$@ $< OBJCOPY := $(TOOLCHAIN_PREFIX)objcopy
$(CC) -nostartfiles -nostdlib -ffreestanding -march=rv32i_zicsr -mabi=ilp32 -Tlink.ld -o$@ $< OBJDUMP := $(TOOLCHAIN_PREFIX)objdump
%.bin: %.elf 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 $< $@ $(OBJCOPY) -O binary $< $@
%.hex: %.bin
$(TARGET).hex: $(TARGET).bin
hexdump -v -e '1/1 "%02x\n"' $< > $@ 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: clean:
rm -f *.elf *.bin *.hex rm -f $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).coe $(TARGET).mif \
$(TARGET).elf.asm $(TARGET).map $(OBJS)

View File

@@ -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

22
sw/blinky/blinky.c Normal file
View File

@@ -0,0 +1,22 @@
#include <stdint.h>
#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);
}
}

View File

@@ -1,10 +1,33 @@
OUTPUT_ARCH("riscv") OUTPUT_ARCH("riscv")
ENTRY(_start) ENTRY(_start)
MEMORY
{
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8K
}
SECTIONS SECTIONS
{ {
. = 0x00000000; .text :
.text : { *(.text) } {
.data : { *(.data) } KEEP(*(.text.init))
.bss : { *(.bss) } *(.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);
} }

23
sw/blinky/start.s Normal file
View File

@@ -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