134 lines
3.3 KiB
Systemverilog
134 lines
3.3 KiB
Systemverilog
`timescale 1ns/100ps
|
|
`default_nettype none
|
|
`include "assert.vh"
|
|
|
|
/*
|
|
* MCP3201 controller
|
|
* Multichannel and sample rate accurate version.
|
|
*/
|
|
module mcp3201_ma #(parameter CHANNELS = 1,
|
|
parameter CLOCK_FREQ = 12000000,
|
|
parameter SCLK_FREQ = 1000000,
|
|
parameter SAMPLE_RATE = 44100)
|
|
(input wire clock,
|
|
input wire reset,
|
|
|
|
output reg spi_clk_o,
|
|
output reg spi_ssn_o,
|
|
input wire [CHANNELS-1:0] spi_miso_i,
|
|
|
|
output reg [CHANNELS*12-1:0] data_o,
|
|
output reg strb_o);
|
|
|
|
initial begin
|
|
`assert(CHANNELS > 0);
|
|
end
|
|
|
|
/* SCLK frequency not need accuracy */
|
|
localparam SCLK_PERIOD = CLOCK_FREQ/SCLK_FREQ;
|
|
localparam SCLK_CW = $clog2(SCLK_PERIOD);
|
|
|
|
logic [SCLK_CW-1:0] sclk_cnt;
|
|
logic sclk_posedge;
|
|
|
|
/* Make SPI SCLK */
|
|
always_ff @(posedge clock)
|
|
if (reset | spi_ssn_o) begin
|
|
spi_clk_o <= 1'b1;
|
|
sclk_cnt <= '0;
|
|
sclk_posedge <= 1'b0;
|
|
end
|
|
else begin
|
|
sclk_posedge <= 1'b0;
|
|
sclk_cnt <= sclk_cnt + 1'b1;
|
|
|
|
if (sclk_cnt == SCLK_CW'(SCLK_PERIOD/2))
|
|
spi_clk_o <= 1'b0;
|
|
else
|
|
if (sclk_cnt == SCLK_CW'(SCLK_PERIOD-1)) begin
|
|
spi_clk_o <= 1'b1;
|
|
sclk_cnt <= '0;
|
|
sclk_posedge <= 1'b1;
|
|
end
|
|
end
|
|
|
|
/* Sample rate need more accuracy */
|
|
localparam SRATE_PERIOD = $rtoi($floor($itor(CLOCK_FREQ)/$itor(SAMPLE_RATE) + 0.5));
|
|
localparam SRATE_CW = $clog2(SRATE_PERIOD);
|
|
|
|
logic [SRATE_CW-1:0] srate_cnt;
|
|
logic sample;
|
|
|
|
always_ff @(posedge clock, posedge reset)
|
|
if (reset) begin
|
|
sample <= 1'b0;
|
|
srate_cnt <= '0;
|
|
end
|
|
else
|
|
if (srate_cnt == SRATE_CW'(SRATE_PERIOD-1)) begin
|
|
sample <= 1'b1;
|
|
srate_cnt <= '0;
|
|
end
|
|
else begin
|
|
sample <= 1'b0;
|
|
srate_cnt <= srate_cnt + 1'b1;
|
|
end
|
|
|
|
/* Receive data FSM */
|
|
enum int unsigned {
|
|
ST_RELAX = 0,
|
|
ST_SHIFT,
|
|
ST_STROBE
|
|
} state;
|
|
|
|
logic [3:0] bit_cnt;
|
|
logic [11:0] data_sr[CHANNELS];
|
|
integer i;
|
|
|
|
always_ff @(posedge clock, posedge reset)
|
|
if (reset) begin
|
|
state <= ST_RELAX;
|
|
bit_cnt <= '0;
|
|
spi_ssn_o <= 1'b1;
|
|
strb_o <= 1'b0;
|
|
data_o <= '0;
|
|
|
|
for (i = 0; i < CHANNELS; i ++)
|
|
data_sr[i] <= '0;
|
|
end
|
|
else begin
|
|
strb_o <= 1'b0;
|
|
|
|
case (state)
|
|
ST_RELAX:
|
|
if (sample) begin
|
|
bit_cnt <= '0;
|
|
spi_ssn_o <= 1'b0;
|
|
state <= ST_SHIFT;
|
|
end
|
|
|
|
ST_SHIFT:
|
|
if (sclk_posedge) begin
|
|
for (i = 0; i < CHANNELS; i ++)
|
|
data_sr[i] <= { data_sr[i][10:0], spi_miso_i[i] };
|
|
|
|
bit_cnt <= bit_cnt + 1'b1;
|
|
|
|
if (bit_cnt == 4'd14) begin
|
|
spi_ssn_o <= 1'b1;
|
|
state <= ST_STROBE;
|
|
end
|
|
end
|
|
|
|
ST_STROBE: begin
|
|
for (i = 0; i < CHANNELS; i ++)
|
|
data_o[i*12 +: 12] <= data_sr[i];
|
|
|
|
strb_o <= 1'b1;
|
|
state <= ST_RELAX;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
endmodule // mcp3201_ma
|