185 lines
4.6 KiB
Verilog
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
|