upsilon/gateware/rtl/waveform/waveform_sim.v

155 lines
3.2 KiB
Verilog

/* Copyright 2024 (C) Peter McGoron
*
* This file is a part of Upsilon, a free and open source software project.
* For license terms, refer to the files in `doc/copying` in the Upsilon
* source distribution.
*/
module waveform_sim #(
parameter RAM_START_ADDR = 32'h0,
parameter SPI_START_ADDR = 32'h10000000,
parameter COUNTER_MAX_WID = 16,
parameter TIMER_WID = 16
) (
input clk,
/* Waveform output control */
input run,
input force_stop,
output [COUNTER_MAX_WID-1:0] cntr,
input do_loop,
output finished,
output ready,
input [COUNTER_MAX_WID-1:0] wform_size,
output [TIMER_WID-1:0] timer,
input [TIMER_WID-1:0] timer_spacing,
/* data requests to Verilator */
output reg [32-1:0] offset,
output reg [32-1:0] spi_data,
input [32-1:0] ram_data,
output reg [1:0] enable,
input ram_finished,
/* Misc */
input [32-1:0] spi_max_wait
);
wire [32-1:0] wb_adr;
wire wb_cyc;
wire wb_we;
wire wb_stb;
wire [32-1:0] wb_dat_w;
reg [32-1:0] wb_dat_r;
wire [4-1:0] wb_sel;
reg wb_ack = 0;
waveform #(
.RAM_START_ADDR(RAM_START_ADDR),
.SPI_START_ADDR(SPI_START_ADDR),
.COUNTER_MAX_WID(COUNTER_MAX_WID),
.TIMER_WID(TIMER_WID)
) wf (
.clk(clk),
.run(run),
.force_stop(force_stop),
.cntr(cntr),
.do_loop(do_loop),
.finished(finished),
.ready(ready),
.wform_size(wform_size),
.timer(timer),
.timer_spacing(timer_spacing),
.wb_adr(wb_adr),
.wb_cyc(wb_cyc),
.wb_we(wb_we),
.wb_stb(wb_stb),
.wb_dat_w(wb_dat_w),
.wb_dat_r(wb_dat_r),
.wb_ack(wb_ack),
.wb_sel(wb_sel)
);
reg [32-1:0] spi_cntr = 0;
reg spi_armed = 0;
reg spi_running = 0;
reg spi_ready_to_arm = 1;
reg spi_finished = 0;
/* SPI Delay simulation */
always @ (posedge clk) if ((spi_armed || spi_running) && !spi_finished) begin
spi_running <= 1;
spi_ready_to_arm <= 0;
if (spi_cntr == spi_max_wait) begin
spi_cntr <= 0;
spi_finished <= 1;
end else begin
spi_cntr <= spi_cntr + 1;
end
end else if (spi_finished) begin
spi_running <= 0;
if (!spi_armed) begin
spi_finished <= 0;
spi_ready_to_arm <= 1;
end
end
/* Bus handlers */
always @* if (wb_we && wb_sel != 4'b1111)
$error("Write request without writing entire word");
always @ (posedge clk) if (wb_cyc & wb_stb & ~wb_ack) begin
if (wb_adr >= SPI_START_ADDR) case (wb_adr)
SPI_START_ADDR | 32'h4: if (wb_we) begin
spi_armed <= wb_dat_w[0];
wb_ack <= 1;
end else begin
wb_dat_r[0] <= spi_armed;
wb_ack <= 1;
end
SPI_START_ADDR | 32'hC: begin
if (!wb_we) $error("Waveform should never read the to_slave register");
/* Write SPI Data to verilator immediately, but have a counter
* running in the background to simulate SPI transfer */
spi_data <= wb_dat_w;
enable <= 2'b10;
if (ram_finished) begin
enable <= 0;
wb_ack <= 1;
end
end
SPI_START_ADDR | 32'h10: begin
if (wb_we) $error("Blocking SPI status check is read only register");
if (spi_ready_to_arm || spi_finished) begin
wb_dat_r[0] <= spi_ready_to_arm;
wb_dat_r[1] <= spi_finished;
wb_ack <= 1;
end
end
default: begin
$error("Invalid SPI address");
end
endcase else begin
offset <= wb_adr;
enable <= 2'b01;
if (ram_finished) begin
wb_dat_r <= ram_data;
enable <= 0;
wb_ack <= 1;
end
end
end else if (~wb_cyc) begin
wb_ack <= 0;
end
initial begin
$dumpfile("waveform.fst");
$dumpvars;
end
endmodule