Add multichannel and sample rate accurate version
This commit is contained in:
parent
00b07d114f
commit
622191172e
@ -1,6 +1,8 @@
|
||||
`timescale 1ns/100ps
|
||||
`default_nettype none
|
||||
|
||||
`define MULTICHANNEL_VERSION
|
||||
|
||||
module dual_mcp3201_pmod #(parameter CLOCK_FREQ = 25000000,
|
||||
parameter SAMPLE_RATE = 50000)
|
||||
(input wire clock,
|
||||
@ -19,6 +21,29 @@ module dual_mcp3201_pmod #(parameter CLOCK_FREQ = 25000000,
|
||||
output wire [11:0] ldata,
|
||||
output wire lstrb);
|
||||
|
||||
`ifdef MULTICHANNEL_VERSION
|
||||
/* Multichannel and sample rate accurate version */
|
||||
|
||||
localparam SPI_SCLK_FREQ = 1000000;
|
||||
|
||||
mcp3201_ma #(.CHANNELS(2),
|
||||
.CLOCK_FREQ(CLOCK_FREQ),
|
||||
.SCLK_FREQ(SPI_SCLK_FREQ),
|
||||
.SAMPLE_RATE(SAMPLE_RATE)) adcs
|
||||
(.clock, .reset,
|
||||
.spi_clk_o(radc_clk),
|
||||
.spi_ssn_o(radc_ssn),
|
||||
.spi_miso_i({ radc_dat, ladc_dat }),
|
||||
.data_o({ rdata, ldata }),
|
||||
.strb_o(rstrb));
|
||||
|
||||
assign ladc_clk = radc_clk;
|
||||
assign ladc_ssn = radc_ssn;
|
||||
assign lstrb = rstrb;
|
||||
|
||||
`else
|
||||
/* Single channel inaccurate version */
|
||||
|
||||
localparam MCP3201_CLOCK_PER_SAMPLE = 17;
|
||||
localparam SCLK_FREQ = SAMPLE_RATE * MCP3201_CLOCK_PER_SAMPLE;
|
||||
|
||||
@ -47,5 +72,6 @@ module dual_mcp3201_pmod #(parameter CLOCK_FREQ = 25000000,
|
||||
|
||||
assign ladc_clk = spi_clk;
|
||||
assign radc_clk = spi_clk;
|
||||
`endif
|
||||
|
||||
endmodule // dual_mcp3201_pmod
|
||||
|
||||
131
source/mcp3201_ma.sv
Normal file
131
source/mcp3201_ma.sv
Normal file
@ -0,0 +1,131 @@
|
||||
`timescale 1ns/100ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* 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
|
||||
if (CHANNELS < 1)
|
||||
$error("Channels count must be > 1");
|
||||
|
||||
/* 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 = integer'($floor(real'(CLOCK_FREQ)/real'(SAMPLE_RATE) + 0.5));
|
||||
localparam SRATE_CW = $clog2(SRATE_PERIOD);
|
||||
|
||||
logic [SRATE_CW-1:0] srate_cnt;
|
||||
logic sample;
|
||||
|
||||
always_ff @(posedge clock)
|
||||
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];
|
||||
|
||||
always_ff @(posedge clock)
|
||||
if (reset) begin
|
||||
state <= ST_RELAX;
|
||||
bit_cnt <= '0;
|
||||
spi_ssn_o <= 1'b1;
|
||||
strb_o <= 1'b0;
|
||||
data_o <= '0;
|
||||
|
||||
for (int 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 (int 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 (int 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
|
||||
5
testbench/.dir-locals.el
Normal file
5
testbench/.dir-locals.el
Normal file
@ -0,0 +1,5 @@
|
||||
;;; Directory Local Variables
|
||||
;;; For more information see (info "(emacs) Directory Variables")
|
||||
|
||||
((verilog-mode . ((flycheck-verilator-include-path . ("." "../source"))
|
||||
(verilog-library-directories . ("." "../source")))))
|
||||
@ -1,10 +1,9 @@
|
||||
VC = iverilog
|
||||
VI = vvp
|
||||
|
||||
SOURCES = ../rtl/mcp3201.sv ../rtl/spi_sclk_gen.sv
|
||||
SOURCES = ../source/mcp3201.sv ../source/mcp3201_ma.sv
|
||||
|
||||
VFLAGS = -g2012
|
||||
|
||||
TBS = $(wildcard tb_*.sv)
|
||||
DEFINES = -D TESTBENCH
|
||||
VCDDEPS = $(TBS:.sv=.vcd)
|
||||
|
||||
83
testbench/tb_mcp3201_ma.sv
Normal file
83
testbench/tb_mcp3201_ma.sv
Normal file
@ -0,0 +1,83 @@
|
||||
`timescale 1ns/100ps
|
||||
|
||||
module tb_mcp3201_ma;
|
||||
logic clock = 1'b0;
|
||||
logic reset = 1'b1;
|
||||
|
||||
/* Master clock 50MHz (20ns period) */
|
||||
always #(20ns/2) clock <= ~clock;
|
||||
|
||||
localparam ITERATIONS = 10;
|
||||
localparam CHANNELS = 3;
|
||||
|
||||
logic spi_clk;
|
||||
logic spi_ssn;
|
||||
logic [CHANNELS-1:0] spi_miso;
|
||||
|
||||
logic [CHANNELS*12-1:0] data;
|
||||
logic strb;
|
||||
|
||||
mcp3201_ma #(.CHANNELS(CHANNELS),
|
||||
.CLOCK_FREQ(50000000),
|
||||
.SCLK_FREQ(1000000),
|
||||
.SAMPLE_RATE(44100)) DUT
|
||||
(.clock, .reset,
|
||||
.spi_clk_o(spi_clk),
|
||||
.spi_ssn_o(spi_ssn),
|
||||
.spi_miso_i(spi_miso),
|
||||
.data_o(data),
|
||||
.strb_o(strb));
|
||||
|
||||
always_ff @ (posedge clock)
|
||||
if (strb) begin
|
||||
$display("");
|
||||
|
||||
for (int i = 0; i < CHANNELS; i ++)
|
||||
$display("o_CH%d: %15b", i, data[i*12 +: 12]);
|
||||
end
|
||||
|
||||
logic [14:0] ds[CHANNELS];
|
||||
|
||||
initial begin
|
||||
reset = 1'b1;
|
||||
repeat(10) @(posedge clock);
|
||||
reset = 1'b0;
|
||||
|
||||
repeat(50) @(posedge clock);
|
||||
|
||||
for (int i = 0; i < ITERATIONS; i ++) begin
|
||||
|
||||
wait(spi_ssn == 1'b0);
|
||||
|
||||
$display("");
|
||||
|
||||
for (int c = 0; c < CHANNELS; c ++) begin
|
||||
ds[c][14:13] = 2'bzz;
|
||||
ds[c][12] = 1'b0;
|
||||
ds[c][11:0] = $random;
|
||||
|
||||
$display("i_CH%d: %15b", c, ds[c][11:0]);
|
||||
end
|
||||
|
||||
for (int n = 0; n < 15; n ++) begin
|
||||
@(negedge spi_clk);
|
||||
|
||||
for (int c = 0; c < CHANNELS; c ++) begin
|
||||
spi_miso[c] = ds[c][14];
|
||||
ds[c] = ds[c] << 1;
|
||||
end
|
||||
end
|
||||
|
||||
wait(spi_ssn == 1'b1);
|
||||
end
|
||||
|
||||
repeat(50) @(posedge clock);
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
$dumpfile("tb_mcp3201_ma.vcd");
|
||||
$dumpvars;
|
||||
end
|
||||
|
||||
endmodule // tb_mcp3201_ma
|
||||
Loading…
x
Reference in New Issue
Block a user