mirror of
https://github.com/pConst/basic_verilog.git
synced 2025-01-14 06:42:54 +08:00
Drastically simplyfied universal SPI master
This commit is contained in:
parent
fbb6ecff17
commit
fe43d58156
309
spi_master.sv
309
spi_master.sv
@ -6,34 +6,27 @@
|
|||||||
// INFO ------------------------------------------------------------------------
|
// INFO ------------------------------------------------------------------------
|
||||||
// Universal spi master
|
// Universal spi master
|
||||||
//
|
//
|
||||||
// * Supports all SPI bus modes
|
// * Supports following SPI bus modes
|
||||||
// mode 0 (CPOL = 0, CPHA = 0)
|
// mode 0 (CPOL = 0, CPHA = 0)
|
||||||
// mode 1 (CPOL = 0, CPHA = 1)
|
|
||||||
// mode 2 (CPOL = 1, CPHA = 0)
|
// mode 2 (CPOL = 1, CPHA = 0)
|
||||||
// mode 3 (CPOL = 1, CPHA = 1)
|
//
|
||||||
// * Moreover, universal spi master features separate parameters to set
|
|
||||||
// clock edge to update data by spi master and
|
|
||||||
// clock edge to latch data by spi master
|
|
||||||
// * Spi clock can be made free-running (some slaves require that)
|
// * Spi clock can be made free-running (some slaves require that)
|
||||||
// * OE pin for bidirectional buffer connection, in case DO and DI pins are combined
|
// * OE pin for bidirectional buffer connection, in case DO and DI pins are combined
|
||||||
//
|
//
|
||||||
// * Universal spi master successfully synthesize at clk speeds up to 200MHz
|
// * Universal spi master successfully synthesize at clk speeds 200MHz and above
|
||||||
// * That means, that SPI clocks up to 50MHz are supported
|
// * That means, that SPI clocks up to 100MHz are supported
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
/* --- INSTANTIATION TEMPLATE BEGIN ---
|
/* --- INSTANTIATION TEMPLATE BEGIN ---
|
||||||
|
|
||||||
spi_master #(
|
spi_master #(
|
||||||
.WRITE_WIDTH( 8 ),
|
.CPOL( 0 ),
|
||||||
|
.FREE_RUNNING_SPI_CLK( 0 ),
|
||||||
|
.MOSI_DATA_WIDTH( 8 ),
|
||||||
.WRITE_MSB_FIRST( 1 ),
|
.WRITE_MSB_FIRST( 1 ),
|
||||||
.WRITE_DATA_EDGE( 1 ),
|
.MISO_DATA_WIDTH( 8 ),
|
||||||
|
.READ_MSB_FIRST( 1 )
|
||||||
.READ_WIDTH( 8 ),
|
|
||||||
.READ_MSB_FIRST( 1 ),
|
|
||||||
.READ_DATA_EDGE( 1 ),
|
|
||||||
|
|
||||||
.FREE_RUNNING_SPI_CLK( 0 )
|
|
||||||
) SM1 (
|
) SM1 (
|
||||||
.clk( ),
|
.clk( ),
|
||||||
.nrst( ),
|
.nrst( ),
|
||||||
@ -47,9 +40,9 @@ spi_master #(
|
|||||||
|
|
||||||
.clk_pin( ),
|
.clk_pin( ),
|
||||||
.ncs_pin( ),
|
.ncs_pin( ),
|
||||||
.d_out_pin( ),
|
.mosi_pin( ),
|
||||||
.d_oe( ),
|
.oe_pin( ),
|
||||||
.d_in_pin( )
|
.miso_pin( )
|
||||||
);
|
);
|
||||||
|
|
||||||
--- INSTANTIATION TEMPLATE END ---*/
|
--- INSTANTIATION TEMPLATE END ---*/
|
||||||
@ -57,56 +50,53 @@ spi_master #(
|
|||||||
|
|
||||||
module spi_master #( parameter
|
module spi_master #( parameter
|
||||||
|
|
||||||
bit [4:0] WRITE_WIDTH = 8, // data word width in bits
|
bit CPOL = 0, // Clock polarity for SPI interface
|
||||||
|
// 0 - SPI mode 0
|
||||||
|
// data updates on rising edge
|
||||||
|
// data reads on falling edge
|
||||||
|
// 1 - SPI mode 2
|
||||||
|
// data updates on falling edge
|
||||||
|
// data reads on rising edge
|
||||||
|
bit FREE_RUNNING_SPI_CLK = 0, // 0 - clk_pin is active only when ncs_pin = 0
|
||||||
|
// 1 - clk pin is always active
|
||||||
|
bit [5:0] MOSI_DATA_WIDTH = 8, // data word width in bits
|
||||||
bit WRITE_MSB_FIRST = 1, // 0 - LSB first
|
bit WRITE_MSB_FIRST = 1, // 0 - LSB first
|
||||||
// 1 - MSB first
|
// 1 - MSB first
|
||||||
bit WRITE_DATA_EDGE = 0, // 0 - master updates on rising edge
|
bit [5:0] MISO_DATA_WIDTH = 8, // data word width in bits
|
||||||
// slave reads data on falling edge
|
bit READ_MSB_FIRST = 1 // 0 - LSB first
|
||||||
// 1 - master updates on falling edge
|
|
||||||
// slave reads data on rising edge
|
|
||||||
|
|
||||||
bit [4:0] READ_WIDTH = 8, // data word width in bits
|
|
||||||
bit READ_MSB_FIRST = 1, // 0 - LSB first
|
|
||||||
// 1 - MSB first
|
// 1 - MSB first
|
||||||
bit READ_DATA_EDGE = 1, // 0 - slave updates on rising edge
|
|
||||||
// master reads data on falling edge
|
|
||||||
// 1 - slave updates on falling edge
|
|
||||||
// master reads data on rising edge
|
|
||||||
|
|
||||||
bit FREE_RUNNING_SPI_CLK = 0 // 0 - clk_pin is active only when ncs_pin = 0
|
|
||||||
// 1 - clk pin is always active
|
|
||||||
)(
|
)(
|
||||||
input clk, // system clock
|
input clk, // system clock
|
||||||
input nrst, // reset (inversed)
|
input nrst, // reset (inversed)
|
||||||
|
|
||||||
input spi_clk, // prescaler clock
|
input spi_clk, // prescaler clock
|
||||||
// spi_clk must be >= 4 clk cycles
|
// spi_clk must be >= 2 clk cycles
|
||||||
// must be synchronous multiple of clk cycles
|
// must be synchronous multiple of clk cycles
|
||||||
|
|
||||||
input spi_wr_cmd, // spi write command, shifting begins on rising edge
|
input spi_wr_cmd, // spi write command, shifting begins on rising edge
|
||||||
input spi_rd_cmd, // spi read command, shifting begins on rising edge
|
input spi_rd_cmd, // spi read command, shifting begins on rising edge
|
||||||
output logic spi_busy, // shifting is active
|
output logic spi_busy, // shifting is active
|
||||||
|
|
||||||
input [WRITE_WIDTH-1:0] mosi_data, // data for shifting out from master
|
input [MOSI_DATA_WIDTH-1:0] mosi_data, // data for shifting out from master
|
||||||
output logic [READ_WIDTH-1:0] miso_data = 0, // shifted in data from slave
|
output logic [MISO_DATA_WIDTH-1:0] miso_data, // shifted in data from slave
|
||||||
|
|
||||||
output logic clk_pin = 0, // spi master's clock pin
|
output logic clk_pin, // spi master's clock pin
|
||||||
output logic ncs_pin = 1, // spi master's chip select (inversed)
|
output logic ncs_pin = 1, // spi master's chip select (inversed)
|
||||||
output logic d_out_pin = 0, // spi master's data in
|
output logic mosi_pin = 0, // spi master's data in
|
||||||
output logic d_oe = 1, // spi master's output enable
|
output logic oe_pin = 0, // spi master's output enable
|
||||||
// in case of slave has only one SDIO pin
|
// in case of using bidirectional buffer for SDIO pin
|
||||||
input d_in_pin // spi master's data out
|
input miso_pin // spi master's data in
|
||||||
);
|
);
|
||||||
|
|
||||||
// sequence_cntr[7:0]==0 - waiting for spi_wr_cmd or spi_wr_cmd
|
|
||||||
// WRITE_SEQ_START - switching mode or transaction end
|
|
||||||
// WRITE_SEQ_END - waiting for right edge to set first data
|
|
||||||
|
|
||||||
localparam WRITE_SEQ_START = 1;
|
// first extra state for getting command and buffering
|
||||||
localparam WRITE_SEQ_END = WRITE_SEQ_START+2*WRITE_WIDTH;
|
// second extra state to initialize outputs
|
||||||
|
localparam WRITE_SEQ_START = 2;
|
||||||
|
localparam WRITE_SEQ_END = WRITE_SEQ_START+2*MOSI_DATA_WIDTH;
|
||||||
|
|
||||||
|
localparam READ_SEQ_START = WRITE_SEQ_END;
|
||||||
|
localparam READ_SEQ_END = READ_SEQ_START+2*MISO_DATA_WIDTH;
|
||||||
|
|
||||||
localparam READ_SEQ_START = WRITE_SEQ_END+1;
|
|
||||||
localparam READ_SEQ_END = READ_SEQ_START+2*READ_WIDTH;
|
|
||||||
|
|
||||||
logic spi_clk_rise;
|
logic spi_clk_rise;
|
||||||
logic spi_clk_fall;
|
logic spi_clk_fall;
|
||||||
@ -130,195 +120,141 @@ edge_detect ed_cmds [1:0] (
|
|||||||
.both( )
|
.both( )
|
||||||
);
|
);
|
||||||
|
|
||||||
// input synchronizer for d_in_pin in clk domain, 2 cycles delay
|
// no need to synchronize miso pin because that is a slave`s responsibility
|
||||||
// making similar delay for clk edges for read operation timing
|
// to hold stable signal and avoid metastability
|
||||||
logic d_in_pin_d2;
|
|
||||||
logic spi_clk_rise_d2;
|
|
||||||
logic spi_clk_fall_d2;
|
// shifting out is always LSB first
|
||||||
delay #(
|
// optionally reversing miso data if requested
|
||||||
.LENGTH( 2 )
|
logic [MOSI_DATA_WIDTH-1:0] mosi_data_rev;
|
||||||
) d_in_pin_synch [2:0] (
|
reverse_vector #(
|
||||||
.clk( {3{clk}} ),
|
.WIDTH( MOSI_DATA_WIDTH )
|
||||||
.nrst( {3{nrst}} ),
|
) reverse_mosi_data (
|
||||||
.ena( {3{1'b1}} ),
|
.in( mosi_data[MOSI_DATA_WIDTH-1:0] ),
|
||||||
.in( {d_in_pin,spi_clk_rise,spi_clk_fall} ),
|
.out( mosi_data_rev[MOSI_DATA_WIDTH-1:0] )
|
||||||
.out( {d_in_pin_d2,spi_clk_rise_d2,spi_clk_fall_d2} )
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
logic clk_pin_before_inversion; // inversion is optional, see CPOL parameter
|
||||||
logic [7:0] sequence_cntr = 0;
|
logic [7:0] sequence_cntr = 0;
|
||||||
logic rd_nwr = 0; // buffering data direction
|
logic rd_nwr = 0; // buffering data direction
|
||||||
logic [WRITE_WIDTH-1:0] mosi_data_buf = 0; // buffering mosi_data
|
logic [MOSI_DATA_WIDTH-1:0] mosi_data_buf = 0; // buffering mosi_data
|
||||||
|
logic [MISO_DATA_WIDTH-1:0] miso_data_buf = 0; // buffering miso_data
|
||||||
|
|
||||||
always_ff @(posedge clk) begin
|
always_ff @(posedge clk) begin
|
||||||
if( ~nrst ) begin
|
if( ~nrst ) begin
|
||||||
miso_data[READ_WIDTH-1:0] <= 0;
|
clk_pin_before_inversion <= CPOL;
|
||||||
|
|
||||||
clk_pin <= ~WRITE_DATA_EDGE;
|
|
||||||
ncs_pin <= 1'b1;
|
ncs_pin <= 1'b1;
|
||||||
d_out_pin <= 1'b0;
|
mosi_pin <= 1'b0;
|
||||||
d_oe <= 1'b1;
|
oe_pin <= 1'b0;
|
||||||
|
|
||||||
sequence_cntr[7:0] <= 0;
|
sequence_cntr[7:0] <= 0;
|
||||||
rd_nwr <= 0;
|
rd_nwr <= 0;
|
||||||
mosi_data_buf[WRITE_WIDTH-1:0] <= 0;
|
mosi_data_buf[MOSI_DATA_WIDTH-1:0] <= 0;
|
||||||
|
miso_data_buf[MISO_DATA_WIDTH-1:0] <= 0;
|
||||||
end else begin
|
end else begin
|
||||||
|
|
||||||
if( FREE_RUNNING_SPI_CLK ) begin
|
if( FREE_RUNNING_SPI_CLK ) begin
|
||||||
if ( spi_clk_rise ) begin
|
if ( spi_clk_rise ) begin
|
||||||
clk_pin <= 1'b1;
|
clk_pin_before_inversion <= 1'b1;
|
||||||
end
|
end
|
||||||
if( spi_clk_fall ) begin
|
if( spi_clk_fall ) begin
|
||||||
clk_pin <= 1'b0;
|
clk_pin_before_inversion <= 1'b0;
|
||||||
end
|
end
|
||||||
end else begin // FREE_RUNNING_SPI_CLK = 0
|
end else begin // FREE_RUNNING_SPI_CLK = 0
|
||||||
if ( ~ncs_pin &&
|
if ( ~ncs_pin ) begin
|
||||||
// fixing extra clock glitch in the end of read transaction
|
|
||||||
~((sequence_cntr[7:0] == READ_SEQ_END) &&
|
|
||||||
(WRITE_DATA_EDGE != READ_DATA_EDGE)) ) begin
|
|
||||||
if ( spi_clk_rise ) begin
|
if ( spi_clk_rise ) begin
|
||||||
clk_pin <= 1'b1;
|
clk_pin_before_inversion <= 1'b1;
|
||||||
end
|
end
|
||||||
if( spi_clk_fall ) begin
|
if( spi_clk_fall ) begin
|
||||||
clk_pin <= 1'b0;
|
clk_pin_before_inversion <= 1'b0;
|
||||||
end
|
end
|
||||||
end else begin // ncs_pin = 1
|
end else begin // ncs_pin = 1
|
||||||
clk_pin <= ~WRITE_DATA_EDGE;
|
clk_pin_before_inversion <= CPOL;
|
||||||
end
|
end
|
||||||
end
|
end // if( FREE_RUNNING_SPI_CLK )
|
||||||
|
|
||||||
// WRITE =======================================================================
|
// WRITE =======================================================================
|
||||||
|
|
||||||
// sequence start condition
|
// sequence start condition
|
||||||
|
//*cmd_rise signals are NOT synchronous with spi_clk edges
|
||||||
if( sequence_cntr[7:0]==0 && (spi_wr_cmd_rise || spi_rd_cmd_rise) ) begin
|
if( sequence_cntr[7:0]==0 && (spi_wr_cmd_rise || spi_rd_cmd_rise) ) begin
|
||||||
// outputs should NOT be updated here
|
|
||||||
if( spi_rd_cmd_rise ) begin
|
if( spi_rd_cmd_rise ) begin
|
||||||
rd_nwr <= 1'b1;
|
rd_nwr <= 1'b1;
|
||||||
end else begin
|
end else begin
|
||||||
rd_nwr <= 1'b0;
|
rd_nwr <= 1'b0;
|
||||||
end
|
end
|
||||||
sequence_cntr[7:0] <= 1;
|
|
||||||
// buffering mosi_data to avoid data change after shift_cmd issued
|
// buffering mosi_data to avoid data change after shift_cmd issued
|
||||||
mosi_data_buf[WRITE_WIDTH-1:0] <= mosi_data[WRITE_WIDTH-1:0];
|
if( WRITE_MSB_FIRST ) begin
|
||||||
|
mosi_data_buf[MOSI_DATA_WIDTH-1:0] <= mosi_data_rev[MOSI_DATA_WIDTH-1:0];
|
||||||
|
end else begin
|
||||||
|
mosi_data_buf[MOSI_DATA_WIDTH-1:0] <= mosi_data[MOSI_DATA_WIDTH-1:0];
|
||||||
|
end
|
||||||
|
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// second step of initialization, updating outputs synchronously with spi_clk edge
|
||||||
|
if( sequence_cntr[7:0]==1 && spi_clk_rise ) begin
|
||||||
|
ncs_pin <= 1'b0;
|
||||||
|
oe_pin <= 1'b1;
|
||||||
|
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
||||||
end
|
end
|
||||||
|
|
||||||
// clocking out data
|
// clocking out data
|
||||||
if( sequence_cntr[7:0]>=WRITE_SEQ_START && sequence_cntr[7:0]<WRITE_SEQ_END ) begin
|
if( sequence_cntr[7:0]>=WRITE_SEQ_START && sequence_cntr[7:0]<WRITE_SEQ_END ) begin
|
||||||
|
|
||||||
if( WRITE_DATA_EDGE ) begin
|
// we should omit this to start sequence on specific edge
|
||||||
// we should omit this edge when sequence_cntr[7:0]==WRITE_SEQ_START
|
if ( spi_clk_rise ) begin
|
||||||
if ( spi_clk_rise && (sequence_cntr[7:0]!=WRITE_SEQ_START) ) begin
|
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
||||||
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
end
|
||||||
end
|
if( spi_clk_fall ) begin
|
||||||
if( spi_clk_fall ) begin
|
// changing mosi_pin
|
||||||
// changing d_out_pin
|
mosi_pin <= mosi_data_buf[0];
|
||||||
d_out_pin <= mosi_data_buf[WRITE_WIDTH-1];
|
// shifting out data is alvays LSB first
|
||||||
// shifting out data
|
mosi_data_buf[MOSI_DATA_WIDTH-1:0] <= {1'b0,mosi_data_buf[MOSI_DATA_WIDTH-1:1]};
|
||||||
if( WRITE_MSB_FIRST ) begin
|
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
||||||
mosi_data_buf[WRITE_WIDTH-1:0] <= {mosi_data_buf[WRITE_WIDTH-2:0],1'b0};
|
end
|
||||||
end else begin
|
|
||||||
mosi_data_buf[WRITE_WIDTH-1:0] <= {1'b0,mosi_data_buf[WRITE_WIDTH-1:1]};
|
|
||||||
end
|
|
||||||
// spi output starts on WRITE_DATA_EDGE edge, to set first data
|
|
||||||
if( sequence_cntr[7:0]==WRITE_SEQ_START ) begin
|
|
||||||
ncs_pin <= 1'b0;
|
|
||||||
d_oe <= 1'b1;
|
|
||||||
end
|
|
||||||
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
|
||||||
end
|
|
||||||
end else begin // WRITE_DATA_EDGE == 0
|
|
||||||
if ( spi_clk_rise ) begin
|
|
||||||
// changing d_out_pin
|
|
||||||
d_out_pin <= mosi_data_buf[WRITE_WIDTH-1];
|
|
||||||
// shifting out data
|
|
||||||
if( WRITE_MSB_FIRST ) begin
|
|
||||||
mosi_data_buf[WRITE_WIDTH-1:0] <= {mosi_data_buf[WRITE_WIDTH-2:0],1'b0};
|
|
||||||
end else begin
|
|
||||||
mosi_data_buf[WRITE_WIDTH-1:0] <= {1'b0,mosi_data_buf[WRITE_WIDTH-1:1]};
|
|
||||||
end
|
|
||||||
// spi output starts on WRITE_DATA_EDGE edge, to set first data
|
|
||||||
if( sequence_cntr[7:0]==WRITE_SEQ_START ) begin
|
|
||||||
ncs_pin <= 1'b0;
|
|
||||||
d_oe <= 1'b1;
|
|
||||||
end
|
|
||||||
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
|
||||||
end
|
|
||||||
// we should omit this edge when sequence_cntr[7:0]==WRITE_SEQ_START
|
|
||||||
if( spi_clk_fall && (sequence_cntr[7:0]!=WRITE_SEQ_START) ) begin
|
|
||||||
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
|
||||||
end
|
|
||||||
end // if( WRITE_DATA_EDGE )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
// waiting for valid edge to switch direction
|
// waiting for valid edge to switch direction
|
||||||
if( ~rd_nwr ) begin
|
if( ~rd_nwr ) begin
|
||||||
// end of write transaction
|
// end of write transaction
|
||||||
// resetting shifter to default state
|
// resetting shifter to default state
|
||||||
if( sequence_cntr[7:0]==WRITE_SEQ_END &&
|
if( sequence_cntr[7:0]==WRITE_SEQ_END && spi_clk_fall ) begin
|
||||||
( (~WRITE_DATA_EDGE && spi_clk_rise) ||
|
|
||||||
(WRITE_DATA_EDGE && spi_clk_fall)) ) begin
|
|
||||||
ncs_pin <= 1'b1;
|
ncs_pin <= 1'b1;
|
||||||
d_out_pin <= 1'b0;
|
mosi_pin <= 1'b0;
|
||||||
d_oe <= 1'b1;
|
oe_pin <= 1'b0;
|
||||||
sequence_cntr[7:0] <= 0;
|
sequence_cntr[7:0] <= 0;
|
||||||
end
|
end
|
||||||
end else begin
|
end else begin
|
||||||
if( sequence_cntr[7:0]==WRITE_SEQ_END &&
|
if( sequence_cntr[7:0]==WRITE_SEQ_END && spi_clk_fall ) begin
|
||||||
( (~WRITE_DATA_EDGE && spi_clk_rise) ||
|
|
||||||
(WRITE_DATA_EDGE && spi_clk_fall)) ) begin
|
|
||||||
//ncs_pin <= 1'b0;
|
//ncs_pin <= 1'b0;
|
||||||
d_out_pin <= 1'b0;
|
mosi_pin <= 1'b0;
|
||||||
d_oe <= 1'b0;
|
oe_pin <= 1'b0;
|
||||||
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
||||||
end
|
end
|
||||||
|
|
||||||
// READ ========================================================================
|
// READ ========================================================================
|
||||||
|
|
||||||
// In some combinations of WRITE_DATA_EDGE and READ_DATA_EDGE and slave timing -
|
|
||||||
// we will get false first bit(s) when reading data. That is not a bug. To get valid
|
|
||||||
// read data - increment READ_WIDTH and ommit first received bit(s)
|
|
||||||
|
|
||||||
|
|
||||||
// clocking in data
|
// clocking in data
|
||||||
// spi_clk edges and d_in_pin are 2 cycles delayed
|
|
||||||
if( sequence_cntr[7:0]>=READ_SEQ_START && sequence_cntr[7:0]<READ_SEQ_END ) begin
|
if( sequence_cntr[7:0]>=READ_SEQ_START && sequence_cntr[7:0]<READ_SEQ_END ) begin
|
||||||
|
|
||||||
if( READ_DATA_EDGE ) begin
|
if ( spi_clk_rise ) begin
|
||||||
if ( spi_clk_rise_d2 && (sequence_cntr[7:0]!=READ_SEQ_START) ) begin
|
// shifting in data is alvays LSB first
|
||||||
// shifting in delayed data
|
miso_data_buf[MISO_DATA_WIDTH-1:0] <= {miso_pin,miso_data_buf[MOSI_DATA_WIDTH-1:1]};
|
||||||
if( READ_MSB_FIRST ) begin
|
|
||||||
miso_data[READ_WIDTH-1:0] <= {miso_data[READ_WIDTH-2:0],d_in_pin_d2};
|
|
||||||
end else begin
|
|
||||||
miso_data[READ_WIDTH-1:0] <= {d_in_pin_d2,miso_data[WRITE_WIDTH-1:1]};
|
|
||||||
end
|
|
||||||
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
||||||
end
|
end
|
||||||
// we should omit this edge when sequence_cntr[7:0]==READ_SEQ_START
|
// we should omit this to start sequence on specific edge
|
||||||
if( spi_clk_fall_d2 ) begin
|
if( spi_clk_fall ) begin
|
||||||
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
||||||
end
|
end
|
||||||
end else begin // READ_DATA_EDGE = 0
|
|
||||||
// we should omit this edge when sequence_cntr[7:0]==READ_SEQ_START
|
|
||||||
if( spi_clk_rise_d2 ) begin
|
|
||||||
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
|
||||||
end
|
|
||||||
if ( spi_clk_fall_d2 && (sequence_cntr[7:0]!=READ_SEQ_START) ) begin
|
|
||||||
// shifting in delayed data
|
|
||||||
if( READ_MSB_FIRST ) begin
|
|
||||||
miso_data[READ_WIDTH-1:0] <= {miso_data[READ_WIDTH-2:0],d_in_pin_d2};
|
|
||||||
end else begin
|
|
||||||
miso_data[READ_WIDTH-1:0] <= {d_in_pin_d2,miso_data[WRITE_WIDTH-1:1]};
|
|
||||||
end
|
|
||||||
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
|
|
||||||
end
|
|
||||||
end // if( READ_DATA_EDGE )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
// waiting for valid edge to end read transaction
|
// waiting for valid edge to end read transaction
|
||||||
if( sequence_cntr[7:0]==READ_SEQ_END &&
|
if( sequence_cntr[7:0]==READ_SEQ_END && spi_clk_fall ) begin
|
||||||
( (~READ_DATA_EDGE && spi_clk_rise_d2) ||
|
|
||||||
(READ_DATA_EDGE && spi_clk_fall_d2)) ) begin
|
|
||||||
ncs_pin <= 1'b1;
|
ncs_pin <= 1'b1;
|
||||||
d_out_pin <= 1'b0;
|
mosi_pin <= 1'b0;
|
||||||
d_oe <= 1'b1;
|
oe_pin <= 1'b0;
|
||||||
sequence_cntr[7:0] <= 0;
|
sequence_cntr[7:0] <= 0;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -326,8 +262,37 @@ always_ff @(posedge clk) begin
|
|||||||
end // if( nrst )
|
end // if( nrst )
|
||||||
end // always
|
end // always
|
||||||
|
|
||||||
|
|
||||||
|
logic [MISO_DATA_WIDTH-1:0] miso_data_buf_rev;
|
||||||
|
reverse_vector #(
|
||||||
|
.WIDTH( MISO_DATA_WIDTH )
|
||||||
|
) reverse_miso_data (
|
||||||
|
.in( miso_data_buf[MISO_DATA_WIDTH-1:0] ),
|
||||||
|
.out( miso_data_buf_rev[MISO_DATA_WIDTH-1:0] )
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
always_comb begin
|
always_comb begin
|
||||||
|
|
||||||
|
// CPOL==1 means output clock inversion
|
||||||
|
if( CPOL ) begin
|
||||||
|
// inversion
|
||||||
|
clk_pin = ~clk_pin_before_inversion;
|
||||||
|
end else begin
|
||||||
|
// no inversion
|
||||||
|
clk_pin = clk_pin_before_inversion;
|
||||||
|
end
|
||||||
|
|
||||||
|
// shifting in is always LSB first
|
||||||
|
// optionally reversing miso data if requested
|
||||||
|
if( READ_MSB_FIRST ) begin
|
||||||
|
miso_data[MISO_DATA_WIDTH-1:0] = miso_data_buf_rev[MISO_DATA_WIDTH-1:0];
|
||||||
|
end else begin
|
||||||
|
miso_data[MISO_DATA_WIDTH-1:0] = miso_data_buf[MISO_DATA_WIDTH-1:0];
|
||||||
|
end
|
||||||
|
|
||||||
spi_busy = (sequence_cntr[7:0] != 0);
|
spi_busy = (sequence_cntr[7:0] != 0);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
9
spi_master_tb/compile.sh
Normal file
9
spi_master_tb/compile.sh
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# compile.sh
|
||||||
|
# Konstantin Pavlov, pavlovconst@gmail.com
|
||||||
|
#
|
||||||
|
# This is a support script for launching "Modelsim compile script" on Linux
|
||||||
|
|
||||||
|
|
||||||
|
vsim -do compile.tcl
|
@ -18,6 +18,7 @@ set library_file_list {
|
|||||||
|
|
||||||
work {spi_master_tb.sv
|
work {spi_master_tb.sv
|
||||||
../spi_master.sv
|
../spi_master.sv
|
||||||
|
../reverse_vector.sv
|
||||||
c_rand.v
|
c_rand.v
|
||||||
../edge_detect.sv
|
../edge_detect.sv
|
||||||
../delay.sv
|
../delay.sv
|
||||||
|
@ -1,324 +0,0 @@
|
|||||||
; Copyright 1991-2009 Mentor Graphics Corporation
|
|
||||||
;
|
|
||||||
; All Rights Reserved.
|
|
||||||
;
|
|
||||||
; THIS WORK CONTAINS TRADE SECRET AND PROPRIETARY INFORMATION WHICH IS THE PROPERTY OF
|
|
||||||
; MENTOR GRAPHICS CORPORATION OR ITS LICENSORS AND IS SUBJECT TO LICENSE TERMS.
|
|
||||||
;
|
|
||||||
|
|
||||||
[Library]
|
|
||||||
others = $MODEL_TECH/../modelsim.ini
|
|
||||||
|
|
||||||
; Altera Primitive libraries
|
|
||||||
;
|
|
||||||
; VHDL Section
|
|
||||||
;
|
|
||||||
;
|
|
||||||
; Verilog Section
|
|
||||||
;
|
|
||||||
|
|
||||||
work = work
|
|
||||||
[vcom]
|
|
||||||
; VHDL93 variable selects language version as the default.
|
|
||||||
; Default is VHDL-2002.
|
|
||||||
; Value of 0 or 1987 for VHDL-1987.
|
|
||||||
; Value of 1 or 1993 for VHDL-1993.
|
|
||||||
; Default or value of 2 or 2002 for VHDL-2002.
|
|
||||||
; Default or value of 3 or 2008 for VHDL-2008.
|
|
||||||
VHDL93 = 2002
|
|
||||||
|
|
||||||
; Show source line containing error. Default is off.
|
|
||||||
; Show_source = 1
|
|
||||||
|
|
||||||
; Turn off unbound-component warnings. Default is on.
|
|
||||||
; Show_Warning1 = 0
|
|
||||||
|
|
||||||
; Turn off process-without-a-wait-statement warnings. Default is on.
|
|
||||||
; Show_Warning2 = 0
|
|
||||||
|
|
||||||
; Turn off null-range warnings. Default is on.
|
|
||||||
; Show_Warning3 = 0
|
|
||||||
|
|
||||||
; Turn off no-space-in-time-literal warnings. Default is on.
|
|
||||||
; Show_Warning4 = 0
|
|
||||||
|
|
||||||
; Turn off multiple-drivers-on-unresolved-signal warnings. Default is on.
|
|
||||||
; Show_Warning5 = 0
|
|
||||||
|
|
||||||
; Turn off optimization for IEEE std_logic_1164 package. Default is on.
|
|
||||||
; Optimize_1164 = 0
|
|
||||||
|
|
||||||
; Turn on resolving of ambiguous function overloading in favor of the
|
|
||||||
; "explicit" function declaration (not the one automatically created by
|
|
||||||
; the compiler for each type declaration). Default is off.
|
|
||||||
; The .ini file has Explicit enabled so that std_logic_signed/unsigned
|
|
||||||
; will match the behavior of synthesis tools.
|
|
||||||
Explicit = 1
|
|
||||||
|
|
||||||
; Turn off acceleration of the VITAL packages. Default is to accelerate.
|
|
||||||
; NoVital = 1
|
|
||||||
|
|
||||||
; Turn off VITAL compliance checking. Default is checking on.
|
|
||||||
; NoVitalCheck = 1
|
|
||||||
|
|
||||||
; Ignore VITAL compliance checking errors. Default is to not ignore.
|
|
||||||
; IgnoreVitalErrors = 1
|
|
||||||
|
|
||||||
; Turn off VITAL compliance checking warnings. Default is to show warnings.
|
|
||||||
; Show_VitalChecksWarnings = 0
|
|
||||||
|
|
||||||
; Keep silent about case statement static warnings.
|
|
||||||
; Default is to give a warning.
|
|
||||||
; NoCaseStaticError = 1
|
|
||||||
|
|
||||||
; Keep silent about warnings caused by aggregates that are not locally static.
|
|
||||||
; Default is to give a warning.
|
|
||||||
; NoOthersStaticError = 1
|
|
||||||
|
|
||||||
; Turn off inclusion of debugging info within design units.
|
|
||||||
; Default is to include debugging info.
|
|
||||||
; NoDebug = 1
|
|
||||||
|
|
||||||
; Turn off "Loading..." messages. Default is messages on.
|
|
||||||
; Quiet = 1
|
|
||||||
|
|
||||||
; Turn on some limited synthesis rule compliance checking. Checks only:
|
|
||||||
; -- signals used (read) by a process must be in the sensitivity list
|
|
||||||
; CheckSynthesis = 1
|
|
||||||
|
|
||||||
; Activate optimizations on expressions that do not involve signals,
|
|
||||||
; waits, or function/procedure/task invocations. Default is off.
|
|
||||||
; ScalarOpts = 1
|
|
||||||
|
|
||||||
; Require the user to specify a configuration for all bindings,
|
|
||||||
; and do not generate a compile time default binding for the
|
|
||||||
; component. This will result in an elaboration error of
|
|
||||||
; 'component not bound' if the user fails to do so. Avoids the rare
|
|
||||||
; issue of a false dependency upon the unused default binding.
|
|
||||||
; RequireConfigForAllDefaultBinding = 1
|
|
||||||
|
|
||||||
; Inhibit range checking on subscripts of arrays. Range checking on
|
|
||||||
; scalars defined with subtypes is inhibited by default.
|
|
||||||
; NoIndexCheck = 1
|
|
||||||
|
|
||||||
; Inhibit range checks on all (implicit and explicit) assignments to
|
|
||||||
; scalar objects defined with subtypes.
|
|
||||||
; NoRangeCheck = 1
|
|
||||||
|
|
||||||
[vlog]
|
|
||||||
|
|
||||||
; Turn off inclusion of debugging info within design units.
|
|
||||||
; Default is to include debugging info.
|
|
||||||
; NoDebug = 1
|
|
||||||
|
|
||||||
; Turn off "loading..." messages. Default is messages on.
|
|
||||||
; Quiet = 1
|
|
||||||
|
|
||||||
; Turn on Verilog hazard checking (order-dependent accessing of global vars).
|
|
||||||
; Default is off.
|
|
||||||
; Hazard = 1
|
|
||||||
|
|
||||||
; Turn on converting regular Verilog identifiers to uppercase. Allows case
|
|
||||||
; insensitivity for module names. Default is no conversion.
|
|
||||||
; UpCase = 1
|
|
||||||
|
|
||||||
; Turn on incremental compilation of modules. Default is off.
|
|
||||||
; Incremental = 1
|
|
||||||
|
|
||||||
; Turns on lint-style checking.
|
|
||||||
; Show_Lint = 1
|
|
||||||
|
|
||||||
[vsim]
|
|
||||||
; Simulator resolution
|
|
||||||
; Set to fs, ps, ns, us, ms, or sec with optional prefix of 1, 10, or 100.
|
|
||||||
Resolution = ps
|
|
||||||
|
|
||||||
; User time unit for run commands
|
|
||||||
; Set to default, fs, ps, ns, us, ms, or sec. The default is to use the
|
|
||||||
; unit specified for Resolution. For example, if Resolution is 100ps,
|
|
||||||
; then UserTimeUnit defaults to ps.
|
|
||||||
; Should generally be set to default.
|
|
||||||
UserTimeUnit = default
|
|
||||||
|
|
||||||
; Default run length
|
|
||||||
RunLength = 100
|
|
||||||
|
|
||||||
; Maximum iterations that can be run without advancing simulation time
|
|
||||||
IterationLimit = 5000
|
|
||||||
|
|
||||||
; Directive to license manager:
|
|
||||||
; vhdl Immediately reserve a VHDL license
|
|
||||||
; vlog Immediately reserve a Verilog license
|
|
||||||
; plus Immediately reserve a VHDL and Verilog license
|
|
||||||
; nomgc Do not look for Mentor Graphics Licenses
|
|
||||||
; nomti Do not look for Model Technology Licenses
|
|
||||||
; noqueue Do not wait in the license queue when a license isn't available
|
|
||||||
; viewsim Try for viewer license but accept simulator license(s) instead
|
|
||||||
; of queuing for viewer license
|
|
||||||
; License = plus
|
|
||||||
|
|
||||||
; Stop the simulator after a VHDL/Verilog assertion message
|
|
||||||
; 0 = Note 1 = Warning 2 = Error 3 = Failure 4 = Fatal
|
|
||||||
BreakOnAssertion = 3
|
|
||||||
|
|
||||||
; Assertion Message Format
|
|
||||||
; %S - Severity Level
|
|
||||||
; %R - Report Message
|
|
||||||
; %T - Time of assertion
|
|
||||||
; %D - Delta
|
|
||||||
; %I - Instance or Region pathname (if available)
|
|
||||||
; %% - print '%' character
|
|
||||||
; AssertionFormat = "** %S: %R\n Time: %T Iteration: %D%I\n"
|
|
||||||
|
|
||||||
; Assertion File - alternate file for storing VHDL/Verilog assertion messages
|
|
||||||
; AssertFile = assert.log
|
|
||||||
|
|
||||||
; Default radix for all windows and commands...
|
|
||||||
; Set to symbolic, ascii, binary, octal, decimal, hex, unsigned
|
|
||||||
DefaultRadix = symbolic
|
|
||||||
|
|
||||||
; VSIM Startup command
|
|
||||||
; Startup = do startup.do
|
|
||||||
|
|
||||||
; File for saving command transcript
|
|
||||||
TranscriptFile = transcript
|
|
||||||
|
|
||||||
; File for saving command history
|
|
||||||
; CommandHistory = cmdhist.log
|
|
||||||
|
|
||||||
; Specify whether paths in simulator commands should be described
|
|
||||||
; in VHDL or Verilog format.
|
|
||||||
; For VHDL, PathSeparator = /
|
|
||||||
; For Verilog, PathSeparator = .
|
|
||||||
; Must not be the same character as DatasetSeparator.
|
|
||||||
PathSeparator = /
|
|
||||||
|
|
||||||
; Specify the dataset separator for fully rooted contexts.
|
|
||||||
; The default is ':'. For example, sim:/top
|
|
||||||
; Must not be the same character as PathSeparator.
|
|
||||||
DatasetSeparator = :
|
|
||||||
|
|
||||||
; Disable VHDL assertion messages
|
|
||||||
; IgnoreNote = 1
|
|
||||||
; IgnoreWarning = 1
|
|
||||||
; IgnoreError = 1
|
|
||||||
; IgnoreFailure = 1
|
|
||||||
|
|
||||||
; Default force kind. May be freeze, drive, deposit, or default
|
|
||||||
; or in other terms, fixed, wired, or charged.
|
|
||||||
; A value of "default" will use the signal kind to determine the
|
|
||||||
; force kind, drive for resolved signals, freeze for unresolved signals
|
|
||||||
; DefaultForceKind = freeze
|
|
||||||
|
|
||||||
; If zero, open files when elaborated; otherwise, open files on
|
|
||||||
; first read or write. Default is 0.
|
|
||||||
; DelayFileOpen = 1
|
|
||||||
|
|
||||||
; Control VHDL files opened for write.
|
|
||||||
; 0 = Buffered, 1 = Unbuffered
|
|
||||||
UnbufferedOutput = 0
|
|
||||||
|
|
||||||
; Control the number of VHDL files open concurrently.
|
|
||||||
; This number should always be less than the current ulimit
|
|
||||||
; setting for max file descriptors.
|
|
||||||
; 0 = unlimited
|
|
||||||
ConcurrentFileLimit = 40
|
|
||||||
|
|
||||||
; Control the number of hierarchical regions displayed as
|
|
||||||
; part of a signal name shown in the Wave window.
|
|
||||||
; A value of zero tells VSIM to display the full name.
|
|
||||||
; The default is 0.
|
|
||||||
; WaveSignalNameWidth = 0
|
|
||||||
|
|
||||||
; Turn off warnings from the std_logic_arith, std_logic_unsigned
|
|
||||||
; and std_logic_signed packages.
|
|
||||||
; StdArithNoWarnings = 1
|
|
||||||
|
|
||||||
; Turn off warnings from the IEEE numeric_std and numeric_bit packages.
|
|
||||||
; NumericStdNoWarnings = 1
|
|
||||||
|
|
||||||
; Control the format of the (VHDL) FOR generate statement label
|
|
||||||
; for each iteration. Do not quote it.
|
|
||||||
; The format string here must contain the conversion codes %s and %d,
|
|
||||||
; in that order, and no other conversion codes. The %s represents
|
|
||||||
; the generate_label; the %d represents the generate parameter value
|
|
||||||
; at a particular generate iteration (this is the position number if
|
|
||||||
; the generate parameter is of an enumeration type). Embedded whitespace
|
|
||||||
; is allowed (but discouraged); leading and trailing whitespace is ignored.
|
|
||||||
; Application of the format must result in a unique scope name over all
|
|
||||||
; such names in the design so that name lookup can function properly.
|
|
||||||
; GenerateFormat = %s__%d
|
|
||||||
|
|
||||||
; Specify whether checkpoint files should be compressed.
|
|
||||||
; The default is 1 (compressed).
|
|
||||||
; CheckpointCompressMode = 0
|
|
||||||
|
|
||||||
; List of dynamically loaded objects for Verilog PLI applications
|
|
||||||
; Veriuser = veriuser.sl
|
|
||||||
|
|
||||||
; Specify default options for the restart command. Options can be one
|
|
||||||
; or more of: -force -nobreakpoint -nolist -nolog -nowave
|
|
||||||
; DefaultRestartOptions = -force
|
|
||||||
|
|
||||||
; HP-UX 10.20 ONLY - Enable memory locking to speed up large designs
|
|
||||||
; (> 500 megabyte memory footprint). Default is disabled.
|
|
||||||
; Specify number of megabytes to lock.
|
|
||||||
; LockedMemory = 1000
|
|
||||||
|
|
||||||
; Turn on (1) or off (0) WLF file compression.
|
|
||||||
; The default is 1 (compress WLF file).
|
|
||||||
; WLFCompress = 0
|
|
||||||
|
|
||||||
; Specify whether to save all design hierarchy (1) in the WLF file
|
|
||||||
; or only regions containing logged signals (0).
|
|
||||||
; The default is 0 (save only regions with logged signals).
|
|
||||||
; WLFSaveAllRegions = 1
|
|
||||||
|
|
||||||
; WLF file time limit. Limit WLF file by time, as closely as possible,
|
|
||||||
; to the specified amount of simulation time. When the limit is exceeded
|
|
||||||
; the earliest times get truncated from the file.
|
|
||||||
; If both time and size limits are specified the most restrictive is used.
|
|
||||||
; UserTimeUnits are used if time units are not specified.
|
|
||||||
; The default is 0 (no limit). Example: WLFTimeLimit = {100 ms}
|
|
||||||
; WLFTimeLimit = 0
|
|
||||||
|
|
||||||
; WLF file size limit. Limit WLF file size, as closely as possible,
|
|
||||||
; to the specified number of megabytes. If both time and size limits
|
|
||||||
; are specified then the most restrictive is used.
|
|
||||||
; The default is 0 (no limit).
|
|
||||||
; WLFSizeLimit = 1000
|
|
||||||
|
|
||||||
; Specify whether or not a WLF file should be deleted when the
|
|
||||||
; simulation ends. A value of 1 will cause the WLF file to be deleted.
|
|
||||||
; The default is 0 (do not delete WLF file when simulation ends).
|
|
||||||
; WLFDeleteOnQuit = 1
|
|
||||||
|
|
||||||
; Automatic SDF compilation
|
|
||||||
; Disables automatic compilation of SDF files in flows that support it.
|
|
||||||
; Default is on, uncomment to turn off.
|
|
||||||
; NoAutoSDFCompile = 1
|
|
||||||
|
|
||||||
[lmc]
|
|
||||||
|
|
||||||
[msg_system]
|
|
||||||
; Change a message severity or suppress a message.
|
|
||||||
; The format is: <msg directive> = <msg number>[,<msg number>...]
|
|
||||||
; Examples:
|
|
||||||
; note = 3009
|
|
||||||
; warning = 3033
|
|
||||||
; error = 3010,3016
|
|
||||||
; fatal = 3016,3033
|
|
||||||
; suppress = 3009,3016,3043
|
|
||||||
; The command verror <msg number> can be used to get the complete
|
|
||||||
; description of a message.
|
|
||||||
|
|
||||||
; Control transcripting of elaboration/runtime messages.
|
|
||||||
; The default is to have messages appear in the transcript and
|
|
||||||
; recorded in the wlf file (messages that are recorded in the
|
|
||||||
; wlf file can be viewed in the MsgViewer). The other settings
|
|
||||||
; are to send messages only to the transcript or only to the
|
|
||||||
; wlf file. The valid values are
|
|
||||||
; both {default}
|
|
||||||
; tran {transcript only}
|
|
||||||
; wlf {wlf file only}
|
|
||||||
; msgmode = both
|
|
@ -10,6 +10,13 @@
|
|||||||
|
|
||||||
module spi_master_tb();
|
module spi_master_tb();
|
||||||
|
|
||||||
|
logic clk800;
|
||||||
|
initial begin
|
||||||
|
#0 clk800 = 1'b0;
|
||||||
|
forever
|
||||||
|
#0.625 clk800 = ~clk800;
|
||||||
|
end
|
||||||
|
|
||||||
logic clk200;
|
logic clk200;
|
||||||
initial begin
|
initial begin
|
||||||
#0 clk200 = 1'b0;
|
#0 clk200 = 1'b0;
|
||||||
@ -88,39 +95,28 @@ end
|
|||||||
|
|
||||||
// Module under test ==========================================================
|
// Module under test ==========================================================
|
||||||
|
|
||||||
logic oe1_pin, din1_pin, clk1_pin, clk1_pin_rise, clk1_pin_fall;
|
logic oe1_pin, ncs1_pin, din1_pin, clk1_pin, clk1_pin_rise, clk1_pin_fall;
|
||||||
logic oe2_pin, din2_pin, clk2_pin, clk2_pin_rise, clk2_pin_fall;
|
logic oe2_pin, ncs2_pin, din2_pin, clk2_pin, clk2_pin_rise, clk2_pin_fall;
|
||||||
logic oe3_pin, din3_pin, clk3_pin, clk3_pin_rise, clk3_pin_fall;
|
logic oe3_pin, ncs3_pin, din3_pin, clk3_pin, clk3_pin_rise, clk3_pin_fall;
|
||||||
logic oe4_pin, din4_pin, clk4_pin, clk4_pin_rise, clk4_pin_fall;
|
logic oe4_pin, ncs4_pin, din4_pin, clk4_pin, clk4_pin_rise, clk4_pin_fall;
|
||||||
|
|
||||||
edge_detect ed2[3:0] (
|
|
||||||
.clk( {4{clk200}} ),
|
|
||||||
.nrst( {4{1'b1}} ),
|
|
||||||
.in( {clk1_pin, clk2_pin, clk3_pin, clk4_pin} ),
|
|
||||||
.rising( {clk1_pin_rise, clk2_pin_rise, clk3_pin_rise, clk4_pin_rise} ),
|
|
||||||
.falling( {clk1_pin_fall, clk2_pin_fall, clk3_pin_fall, clk4_pin_fall} ),
|
|
||||||
.both( )
|
|
||||||
);
|
|
||||||
|
|
||||||
reg [7:0] test1_data = 8'b1010_0011;
|
reg [7:0] test1_data = 8'b1010_0011;
|
||||||
reg [7:0] test2_data = 8'b1010_0011;
|
reg [7:0] test2_data = 8'b1010_0011;
|
||||||
reg [7:0] test3_data = 8'b1010_0011;
|
reg [7:0] test3_data = 8'b1010_0011;
|
||||||
reg [7:0] test4_data = 8'b1010_0011;
|
reg [7:0] test4_data = 8'b1010_0011;
|
||||||
|
|
||||||
|
|
||||||
spi_master #(
|
spi_master #(
|
||||||
.WRITE_WIDTH( 8 ),
|
.CPOL( 0 ),
|
||||||
|
.FREE_RUNNING_SPI_CLK( 0 ),
|
||||||
|
.MOSI_DATA_WIDTH( 8 ),
|
||||||
.WRITE_MSB_FIRST( 1 ),
|
.WRITE_MSB_FIRST( 1 ),
|
||||||
.WRITE_DATA_EDGE( 1 ),
|
.MISO_DATA_WIDTH( 8 ),
|
||||||
|
.READ_MSB_FIRST( 1 )
|
||||||
.READ_WIDTH( 8 ),
|
|
||||||
.READ_MSB_FIRST( 1 ),
|
|
||||||
.READ_DATA_EDGE( 1 ),
|
|
||||||
|
|
||||||
.FREE_RUNNING_SPI_CLK( 0 )
|
|
||||||
) SM1 (
|
) SM1 (
|
||||||
.clk( clk200 ),
|
.clk( clk200 ),
|
||||||
.nrst( nrst_once ),
|
.nrst( nrst_once ),
|
||||||
.spi_clk( DerivedClocks[1] ),
|
.spi_clk( DerivedClocks[0] ),
|
||||||
.spi_wr_cmd( 0 ),
|
.spi_wr_cmd( 0 ),
|
||||||
.spi_rd_cmd( start ),
|
.spi_rd_cmd( start ),
|
||||||
.spi_busy( ),
|
.spi_busy( ),
|
||||||
@ -129,26 +125,23 @@ spi_master #(
|
|||||||
.miso_data( ),
|
.miso_data( ),
|
||||||
|
|
||||||
.clk_pin( clk1_pin ),
|
.clk_pin( clk1_pin ),
|
||||||
.ncs_pin( ),
|
.ncs_pin( ncs1_pin ),
|
||||||
.d_out_pin( ),
|
.mosi_pin( ),
|
||||||
.d_oe( oe1_pin ),
|
.oe_pin( oe1_pin ),
|
||||||
.d_in_pin( din1_pin )
|
.miso_pin( din1_pin )
|
||||||
);
|
);
|
||||||
|
|
||||||
spi_master #(
|
spi_master #(
|
||||||
.WRITE_WIDTH( 8 ),
|
.CPOL( 1 ),
|
||||||
|
.FREE_RUNNING_SPI_CLK( 0 ),
|
||||||
|
.MOSI_DATA_WIDTH( 8 ),
|
||||||
.WRITE_MSB_FIRST( 1 ),
|
.WRITE_MSB_FIRST( 1 ),
|
||||||
.WRITE_DATA_EDGE( 0 ),
|
.MISO_DATA_WIDTH( 8 ),
|
||||||
|
.READ_MSB_FIRST( 1 )
|
||||||
.READ_WIDTH( 8 ),
|
|
||||||
.READ_MSB_FIRST( 1 ),
|
|
||||||
.READ_DATA_EDGE( 0 ),
|
|
||||||
|
|
||||||
.FREE_RUNNING_SPI_CLK( 0 )
|
|
||||||
) SM2 (
|
) SM2 (
|
||||||
.clk( clk200 ),
|
.clk( clk200 ),
|
||||||
.nrst( nrst_once ),
|
.nrst( nrst_once ),
|
||||||
.spi_clk( DerivedClocks[1] ),
|
.spi_clk( DerivedClocks[0] ),
|
||||||
.spi_wr_cmd( 0 ),
|
.spi_wr_cmd( 0 ),
|
||||||
.spi_rd_cmd( start ),
|
.spi_rd_cmd( start ),
|
||||||
.spi_busy( ),
|
.spi_busy( ),
|
||||||
@ -157,26 +150,23 @@ spi_master #(
|
|||||||
.miso_data( ),
|
.miso_data( ),
|
||||||
|
|
||||||
.clk_pin( clk2_pin ),
|
.clk_pin( clk2_pin ),
|
||||||
.ncs_pin( ),
|
.ncs_pin( ncs2_pin ),
|
||||||
.d_out_pin( ),
|
.mosi_pin( ),
|
||||||
.d_oe( oe2_pin ),
|
.oe_pin( oe2_pin ),
|
||||||
.d_in_pin( din2_pin )
|
.miso_pin( din2_pin )
|
||||||
);
|
);
|
||||||
|
|
||||||
spi_master #(
|
spi_master #(
|
||||||
.WRITE_WIDTH( 8 ),
|
.CPOL( 0 ),
|
||||||
.WRITE_MSB_FIRST( 1 ),
|
.FREE_RUNNING_SPI_CLK( 1 ),
|
||||||
.WRITE_DATA_EDGE( 1 ),
|
.MOSI_DATA_WIDTH( 8 ),
|
||||||
|
.WRITE_MSB_FIRST( 0 ),
|
||||||
.READ_WIDTH( 8 ),
|
.MISO_DATA_WIDTH( 8 ),
|
||||||
.READ_MSB_FIRST( 1 ),
|
.READ_MSB_FIRST( 0 )
|
||||||
.READ_DATA_EDGE( 0 ),
|
|
||||||
|
|
||||||
.FREE_RUNNING_SPI_CLK( 0 )
|
|
||||||
) SM3 (
|
) SM3 (
|
||||||
.clk( clk200 ),
|
.clk( clk200 ),
|
||||||
.nrst( nrst_once ),
|
.nrst( nrst_once ),
|
||||||
.spi_clk( DerivedClocks[1] ),
|
.spi_clk( DerivedClocks[0] ),
|
||||||
.spi_wr_cmd( 0 ),
|
.spi_wr_cmd( 0 ),
|
||||||
.spi_rd_cmd( start ),
|
.spi_rd_cmd( start ),
|
||||||
.spi_busy( ),
|
.spi_busy( ),
|
||||||
@ -185,26 +175,23 @@ spi_master #(
|
|||||||
.miso_data( ),
|
.miso_data( ),
|
||||||
|
|
||||||
.clk_pin( clk3_pin ),
|
.clk_pin( clk3_pin ),
|
||||||
.ncs_pin( ),
|
.ncs_pin( ncs3_pin ),
|
||||||
.d_out_pin( ),
|
.mosi_pin( ),
|
||||||
.d_oe( oe3_pin ),
|
.oe_pin( oe3_pin ),
|
||||||
.d_in_pin( din3_pin )
|
.miso_pin( din3_pin )
|
||||||
);
|
);
|
||||||
|
|
||||||
spi_master #(
|
spi_master #(
|
||||||
.WRITE_WIDTH( 8 ),
|
.CPOL( 0 ),
|
||||||
.WRITE_MSB_FIRST( 1 ),
|
.FREE_RUNNING_SPI_CLK( 1 ),
|
||||||
.WRITE_DATA_EDGE( 0 ),
|
.MOSI_DATA_WIDTH( 8 ),
|
||||||
|
.WRITE_MSB_FIRST( 0 ),
|
||||||
.READ_WIDTH( 8 ),
|
.MISO_DATA_WIDTH( 8 ),
|
||||||
.READ_MSB_FIRST( 1 ),
|
.READ_MSB_FIRST( 0 )
|
||||||
.READ_DATA_EDGE( 1 ),
|
|
||||||
|
|
||||||
.FREE_RUNNING_SPI_CLK( 0 )
|
|
||||||
) SM4 (
|
) SM4 (
|
||||||
.clk( clk200 ),
|
.clk( clk200 ),
|
||||||
.nrst( nrst_once ),
|
.nrst( nrst_once ),
|
||||||
.spi_clk( DerivedClocks[1] ),
|
.spi_clk( DerivedClocks[0] ),
|
||||||
.spi_wr_cmd( 0 ),
|
.spi_wr_cmd( 0 ),
|
||||||
.spi_rd_cmd( start ),
|
.spi_rd_cmd( start ),
|
||||||
.spi_busy( ),
|
.spi_busy( ),
|
||||||
@ -213,57 +200,68 @@ spi_master #(
|
|||||||
.miso_data( ),
|
.miso_data( ),
|
||||||
|
|
||||||
.clk_pin( clk4_pin ),
|
.clk_pin( clk4_pin ),
|
||||||
.ncs_pin( ),
|
.ncs_pin( ncs4_pin ),
|
||||||
.d_out_pin( ),
|
.mosi_pin( ),
|
||||||
.d_oe( oe4_pin ),
|
.oe_pin( oe4_pin ),
|
||||||
.d_in_pin( din4_pin )
|
.miso_pin( din4_pin )
|
||||||
);
|
);
|
||||||
|
|
||||||
// emulating external divice ==================================================
|
// emulating external divice ==================================================
|
||||||
// that works asynchronously on clk33 clock
|
// that works asynchronously on clk33 clock
|
||||||
|
|
||||||
always_ff @(posedge clk200) begin
|
// clk800 emulates some high-speed "ideal" slave
|
||||||
|
|
||||||
|
edge_detect ed2[3:0] (
|
||||||
|
.clk( {4{clk800}} ),
|
||||||
|
.nrst( {4{1'b1}} ),
|
||||||
|
.in( {clk1_pin, clk2_pin, clk3_pin, clk4_pin} ),
|
||||||
|
.rising( {clk1_pin_rise, clk2_pin_rise, clk3_pin_rise, clk4_pin_rise} ),
|
||||||
|
.falling( {clk1_pin_fall, clk2_pin_fall, clk3_pin_fall, clk4_pin_fall} ),
|
||||||
|
.both( )
|
||||||
|
);
|
||||||
|
|
||||||
|
always_ff @(posedge clk800) begin
|
||||||
if( ~nrst_once) begin
|
if( ~nrst_once) begin
|
||||||
din1_pin <= 0;
|
din1_pin <= 0;
|
||||||
test1_data[7:0] = 8'b1010_0011;
|
test1_data[7:0] = 8'b1010_0011;
|
||||||
end else begin
|
end else begin
|
||||||
if( ~oe1_pin && clk1_pin_rise ) begin
|
if( ~ncs1_pin && ~oe1_pin && clk1_pin_fall ) begin
|
||||||
din1_pin <=test1_data[7];
|
din1_pin <=test1_data[7];
|
||||||
test1_data[7:0] <= {test1_data[6:0],1'b0};
|
test1_data[7:0] <= {test1_data[6:0],1'b0};
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
always_ff @(posedge clk200) begin
|
always_ff @(posedge clk800) begin
|
||||||
if( ~nrst_once) begin
|
if( ~nrst_once) begin
|
||||||
din2_pin <= 0;
|
din2_pin <= 0;
|
||||||
test2_data[7:0] = 8'b1010_0011;
|
test2_data[7:0] = 8'b1010_0011;
|
||||||
end else begin
|
end else begin
|
||||||
if( ~oe2_pin && clk2_pin_fall ) begin
|
if( ~ncs2_pin && ~oe2_pin && clk2_pin_rise ) begin
|
||||||
din2_pin <=test2_data[7];
|
din2_pin <=test2_data[7];
|
||||||
test2_data[7:0] <= {test2_data[6:0],1'b0};
|
test2_data[7:0] <= {test2_data[6:0],1'b0};
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
always_ff @(posedge clk200) begin
|
always_ff @(posedge clk800) begin
|
||||||
if( ~nrst_once) begin
|
if( ~nrst_once) begin
|
||||||
din3_pin <= 0;
|
din3_pin <= 0;
|
||||||
test3_data[7:0] = 8'b1010_0011;
|
test3_data[7:0] = 8'b1010_0011;
|
||||||
end else begin
|
end else begin
|
||||||
if( ~oe3_pin && clk3_pin_fall ) begin
|
if( ~ncs3_pin && ~oe3_pin && clk3_pin_fall ) begin
|
||||||
din3_pin <=test3_data[7];
|
din3_pin <=test3_data[7];
|
||||||
test3_data[7:0] <= {test3_data[6:0],1'b0};
|
test3_data[7:0] <= {test3_data[6:0],1'b0};
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
always_ff @(posedge clk200) begin
|
always_ff @(posedge clk800) begin
|
||||||
if( ~nrst_once) begin
|
if( ~nrst_once) begin
|
||||||
din4_pin <= 0;
|
din4_pin <= 0;
|
||||||
test4_data[7:0] = 8'b1010_0011;
|
test4_data[7:0] = 8'b1010_0011;
|
||||||
end else begin
|
end else begin
|
||||||
if( ~oe4_pin && clk4_pin_fall ) begin
|
if( ~ncs4_pin && ~oe4_pin && clk4_pin_fall ) begin
|
||||||
din4_pin <=test4_data[7];
|
din4_pin <=test4_data[7];
|
||||||
test4_data[7:0] <= {test4_data[6:0],1'b0};
|
test4_data[7:0] <= {test4_data[6:0],1'b0};
|
||||||
end
|
end
|
||||||
|
@ -12,35 +12,35 @@ add wave -noupdate /spi_master_tb/SM1/spi_rd_cmd_rise
|
|||||||
add wave -noupdate /spi_master_tb/SM1/spi_busy
|
add wave -noupdate /spi_master_tb/SM1/spi_busy
|
||||||
add wave -noupdate -radix decimal /spi_master_tb/SM1/sequence_cntr
|
add wave -noupdate -radix decimal /spi_master_tb/SM1/sequence_cntr
|
||||||
add wave -noupdate /spi_master_tb/SM1/rd_nwr
|
add wave -noupdate /spi_master_tb/SM1/rd_nwr
|
||||||
add wave -noupdate -radix binary /spi_master_tb/SM1/data_out
|
add wave -noupdate -radix binary /spi_master_tb/SM1/mosi_data
|
||||||
add wave -noupdate -radix binary /spi_master_tb/SM1/data_in
|
add wave -noupdate -radix binary /spi_master_tb/SM1/miso_data
|
||||||
add wave -noupdate /spi_master_tb/SM1/data_out_buf
|
add wave -noupdate /spi_master_tb/SM1/mosi_data_buf
|
||||||
add wave -noupdate -color Yellow /spi_master_tb/SM1/clk_pin
|
add wave -noupdate -color Yellow /spi_master_tb/SM1/clk_pin
|
||||||
add wave -noupdate -color Yellow /spi_master_tb/SM1/ncs_pin
|
add wave -noupdate -color Yellow /spi_master_tb/SM1/ncs_pin
|
||||||
add wave -noupdate -color Yellow /spi_master_tb/SM1/d_out_pin
|
add wave -noupdate -color Yellow /spi_master_tb/SM1/mosi_pin
|
||||||
add wave -noupdate -color Yellow /spi_master_tb/SM1/d_oe
|
add wave -noupdate -color Yellow /spi_master_tb/SM1/oe_pin
|
||||||
add wave -noupdate -color Yellow /spi_master_tb/SM1/d_in_pin
|
add wave -noupdate -color Yellow /spi_master_tb/SM1/miso_pin
|
||||||
add wave -noupdate /spi_master_tb/SM1/spi_clk_rise_d2
|
add wave -noupdate /spi_master_tb/SM1/spi_clk_rise_d2
|
||||||
add wave -noupdate /spi_master_tb/SM1/spi_clk_fall_d2
|
add wave -noupdate /spi_master_tb/SM1/spi_clk_fall_d2
|
||||||
add wave -noupdate /spi_master_tb/SM1/d_in_pin_d2
|
add wave -noupdate /spi_master_tb/SM1/miso_pin_d2
|
||||||
add wave -noupdate -color {Medium Violet Red} -radix decimal /spi_master_tb/SM2/sequence_cntr
|
add wave -noupdate -color {Medium Violet Red} -radix decimal /spi_master_tb/SM2/sequence_cntr
|
||||||
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/clk_pin
|
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/clk_pin
|
||||||
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/ncs_pin
|
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/ncs_pin
|
||||||
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/d_out_pin
|
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/mosi_pin
|
||||||
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/d_oe
|
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/oe_pin
|
||||||
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/d_in_pin
|
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/miso_pin
|
||||||
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/sequence_cntr
|
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/sequence_cntr
|
||||||
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/clk_pin
|
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/clk_pin
|
||||||
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/ncs_pin
|
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/ncs_pin
|
||||||
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/d_out_pin
|
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/mosi_pin
|
||||||
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/d_oe
|
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/oe_pin
|
||||||
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/d_in_pin
|
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/miso_pin
|
||||||
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/sequence_cntr
|
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/sequence_cntr
|
||||||
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/clk_pin
|
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/clk_pin
|
||||||
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/ncs_pin
|
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/ncs_pin
|
||||||
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/d_out_pin
|
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/mosi_pin
|
||||||
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/d_oe
|
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/oe_pin
|
||||||
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/d_in_pin
|
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/miso_pin
|
||||||
TreeUpdate [SetDefaultTree]
|
TreeUpdate [SetDefaultTree]
|
||||||
WaveRestoreCursors {{Cursor 1} {801886 ps} 0}
|
WaveRestoreCursors {{Cursor 1} {801886 ps} 0}
|
||||||
quietly wave cursor active 1
|
quietly wave cursor active 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user