Add multichannel and sample rate accurate version
This commit is contained in:
parent
00b07d114f
commit
622191172e
@ -1,8 +1,10 @@
|
|||||||
`timescale 1ns/100ps
|
`timescale 1ns/100ps
|
||||||
`default_nettype none
|
`default_nettype none
|
||||||
|
|
||||||
|
`define MULTICHANNEL_VERSION
|
||||||
|
|
||||||
module dual_mcp3201_pmod #(parameter CLOCK_FREQ = 25000000,
|
module dual_mcp3201_pmod #(parameter CLOCK_FREQ = 25000000,
|
||||||
parameter SAMPLE_RATE = 50000)
|
parameter SAMPLE_RATE = 50000)
|
||||||
(input wire clock,
|
(input wire clock,
|
||||||
input wire reset,
|
input wire reset,
|
||||||
|
|
||||||
@ -19,6 +21,29 @@ module dual_mcp3201_pmod #(parameter CLOCK_FREQ = 25000000,
|
|||||||
output wire [11:0] ldata,
|
output wire [11:0] ldata,
|
||||||
output wire lstrb);
|
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 MCP3201_CLOCK_PER_SAMPLE = 17;
|
||||||
localparam SCLK_FREQ = SAMPLE_RATE * MCP3201_CLOCK_PER_SAMPLE;
|
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 ladc_clk = spi_clk;
|
||||||
assign radc_clk = spi_clk;
|
assign radc_clk = spi_clk;
|
||||||
|
`endif
|
||||||
|
|
||||||
endmodule // dual_mcp3201_pmod
|
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
|
VC = iverilog
|
||||||
VI = vvp
|
VI = vvp
|
||||||
|
|
||||||
SOURCES = ../rtl/mcp3201.sv ../rtl/spi_sclk_gen.sv
|
SOURCES = ../source/mcp3201.sv ../source/mcp3201_ma.sv
|
||||||
|
|
||||||
VFLAGS = -g2012
|
VFLAGS = -g2012
|
||||||
|
|
||||||
TBS = $(wildcard tb_*.sv)
|
TBS = $(wildcard tb_*.sv)
|
||||||
DEFINES = -D TESTBENCH
|
DEFINES = -D TESTBENCH
|
||||||
VCDDEPS = $(TBS:.sv=.vcd)
|
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