upsilon/gateware/rtl/waveform/waveform.v

185 lines
4.6 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 #(
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,
output reg[COUNTER_MAX_WID-1:0] cntr,
input do_loop,
output reg finished,
output reg ready,
input [COUNTER_MAX_WID-1:0] wform_size,
output reg [TIMER_WID-1:0] timer,
input [TIMER_WID-1:0] timer_spacing,
/* Bus master */
output reg [32-1:0] wb_adr,
output reg wb_cyc,
output reg wb_we,
output wb_stb,
output [4-1:0] wb_sel,
output reg [32-1:0] wb_dat_w,
input [32-1:0] wb_dat_r,
input wb_ack
);
/* When a Wishbone cycle starts, the output is stable. */
assign wb_stb = wb_cyc;
/* Always write 32 bits */
assign wb_sel = 4'b1111;
localparam CHECK_START = 0;
localparam CHECK_LEN = 1;
localparam WAIT_FINISHED = 2;
localparam READ_RAM = 3;
localparam WAIT_RAM = 4;
localparam WRITE_DAC_DATA_ADR = 5;
localparam WAIT_DAC_DATA_ADR = 6;
localparam WRITE_DAC_ARM_ADR = 7;
localparam WAIT_DAC_ARM_ADR = 8;
localparam WRITE_DAC_DISARM_ADR = 9;
localparam WAIT_DAC_DISARM_ADR = 10;
localparam READ_DAC_FIN_ADR = 11;
localparam WAIT_DAC_FIN_ADR = 12;
localparam WAIT_PERIOD = 13;
reg [4-1:0] state = CHECK_START;
always @ (posedge clk) case (state)
CHECK_START: if (run) begin
cntr <= 0;
ready <= 0;
state <= CHECK_LEN;
end else begin
ready <= 1;
end
CHECK_LEN: if (cntr >= wform_size) begin
if (do_loop) begin
cntr <= 0;
state <= READ_RAM;
end else begin
state <= WAIT_FINISHED;
end
end else begin
state <= READ_RAM;
end
WAIT_FINISHED: if (!run) begin
finished <= 0;
state <= CHECK_START;
end else if (do_loop) begin
state <= READ_RAM;
finished <= 0;
cntr <= 0;
end else begin
finished <= 1;
end
READ_RAM: begin
wb_adr <= RAM_START_ADDR + {16'b0, cntr};
wb_cyc <= 1; /* Always assigned STB when CYC is */
wb_we <= 0;
state <= WAIT_RAM;
end
WAIT_RAM: if (wb_ack) begin
wb_cyc <= 0;
wb_dat_w <= 1 << 20 | wb_dat_r;
state <= WRITE_DAC_DATA_ADR;
end
WRITE_DAC_DATA_ADR: begin
wb_adr <= SPI_START_ADDR + 32'hC;
wb_cyc <= 1;
wb_we <= 1;
state <= WAIT_DAC_DATA_ADR;
end
WAIT_DAC_DATA_ADR: if (wb_ack) begin
wb_cyc <= 0;
/* This is not needed, since the next bus cycle is also a write. */
/* wb_we <= 0; */
state <= WRITE_DAC_ARM_ADR;
end
WRITE_DAC_ARM_ADR: begin
wb_adr <= SPI_START_ADDR + 32'h4;
wb_dat_w[0] <= 1;
wb_cyc <= 1;
/* wb_we <= 1; */
state <= WAIT_DAC_ARM_ADR;
end
WAIT_DAC_ARM_ADR: if (wb_ack) begin
wb_cyc <= 0;
/* This is not needed, since the next bus cycle is also a write. */
/* wb_we <= 0; */
state <= WRITE_DAC_DISARM_ADR;
end
/*
* After arming the SPI core, immediately disarm it.
* The SPI core will continue to execute after being disarmed until
* it completes a transmission cycle. Otherwise the core would spend
* two extra clock cycles if it disarmed after checking that the SPI
* master was done.
*/
WRITE_DAC_DISARM_ADR: begin
wb_adr <= SPI_START_ADDR + 32'h4;
wb_dat_w[0] <= 0;
wb_cyc <= 1;
/* This is not needed, since the previous bus cycle is also a write. */
/* wb_we <= 1; */
state <= WAIT_DAC_DISARM_ADR;
end
WAIT_DAC_DISARM_ADR: if (wb_ack) begin
wb_cyc <= 0;
/* Disable writes because next bus cycle is a write */
wb_we <= 0;
state <= READ_DAC_FIN_ADR;
end
/*
* This core reads from "wait_ready_or_finished", which will
* stall until the SPI core is ready to arm or finished with
* it's transmission.
* If the SPI device is disconnected it will just return 0 at all
* times (see PreemptiveInterface). To avoid getting the core stuck
* the FSM will continue without checking that the DAC is actually
* ready or finished.
*/
READ_DAC_FIN_ADR: begin
wb_adr <= SPI_START_ADDR + 32'h10;
wb_cyc <= 1;
state <= WAIT_DAC_FIN_ADR;
end
WAIT_DAC_FIN_ADR: if (wb_cyc) begin
wb_cyc <= 0;
state <= WAIT_PERIOD;
timer <= 0;
end
/* If the core tells the block to stop running, stop when SPI is not
* transmitting to the DAC.
*
* If you want the module to run until the end of the waveform, turn
* off looping and wait until the waveform ends. If you want the module
* to stop the waveform and keep the DAC in a known state, just turn
* the running flag off. If you need to stop it immediately, flip the
* master switch for the DAC to the main CPU.
*/
WAIT_PERIOD: if (!run) begin
finished <= 0;
state <= CHECK_START;
end else if (timer < timer_spacing) begin
timer <= timer + 1;
end else begin
cntr <= cntr + 1;
state <= CHECK_LEN;
end
default: state <= CHECK_START;
endcase
endmodule