1 Star 5 Fork 5

yhp/basic_verilog

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
spi_master.sv 9.57 KB
一键复制 编辑 原始数据 按行查看 历史
//------------------------------------------------------------------------------
// spi_master.sv
// Konstantin Pavlov, pavlovconst@gmail.com
//------------------------------------------------------------------------------
// INFO ------------------------------------------------------------------------
// Universal spi master
//
// * Supports following SPI bus modes
// mode 0 (CPOL = 0, CPHA = 0)
// mode 2 (CPOL = 1, CPHA = 0)
//
// * 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
//
// * Universal spi master successfully synthesize at clk speeds 200MHz and above
// * That means, that SPI clocks up to 100MHz are supported
//
/* --- INSTANTIATION TEMPLATE BEGIN ---
spi_master #(
.CPOL( 0 ),
.FREE_RUNNING_SPI_CLK( 0 ),
.MOSI_DATA_WIDTH( 8 ),
.WRITE_MSB_FIRST( 1 ),
.MISO_DATA_WIDTH( 8 ),
.READ_MSB_FIRST( 1 )
) SM1 (
.clk( ),
.nrst( ),
.spi_clk( ),
.spi_wr_cmd( ),
.spi_rd_cmd( ),
.spi_busy( ),
.mosi_data( ),
.miso_data( ),
.clk_pin( ),
.ncs_pin( ),
.mosi_pin( ),
.oe_pin( ),
.miso_pin( )
);
--- INSTANTIATION TEMPLATE END ---*/
module spi_master #( parameter
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
// 1 - MSB first
bit [5:0] MISO_DATA_WIDTH = 8, // data word width in bits
bit READ_MSB_FIRST = 1 // 0 - LSB first
// 1 - MSB first
)(
input clk, // system clock
input nrst, // reset (inversed)
input spi_clk, // prescaler clock
// spi_clk must be >= 2 clk cycles
// must be synchronous multiple of clk cycles
input spi_wr_cmd, // spi write 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
input [MOSI_DATA_WIDTH-1:0] mosi_data, // data for shifting out from master
output logic [MISO_DATA_WIDTH-1:0] miso_data, // shifted in data from slave
output logic clk_pin, // spi master's clock pin
output logic ncs_pin = 1, // spi master's chip select (inversed)
output logic mosi_pin = 0, // spi master's data in
output logic oe_pin = 0, // spi master's output enable
// in case of using bidirectional buffer for SDIO pin
input miso_pin // spi master's data in
);
// first extra state for getting command and buffering
// 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;
logic spi_clk_rise;
logic spi_clk_fall;
edge_detect ed_spi_clk (
.clk( clk ),
.nrst( nrst ),
.in( spi_clk ),
.rising( spi_clk_rise ),
.falling( spi_clk_fall ),
.both( )
);
logic spi_wr_cmd_rise;
logic spi_rd_cmd_rise;
edge_detect ed_cmds [1:0] (
.clk( {2{clk}} ),
.nrst( {2{nrst}} ),
.in( {spi_wr_cmd,spi_rd_cmd} ),
.rising( {spi_wr_cmd_rise,spi_rd_cmd_rise} ),
.falling( ),
.both( )
);
// no need to synchronize miso pin because that is a slave`s responsibility
// to hold stable signal and avoid metastability
// shifting out is always LSB first
// optionally reversing miso data if requested
logic [MOSI_DATA_WIDTH-1:0] mosi_data_rev;
reverse_vector #(
.WIDTH( MOSI_DATA_WIDTH )
) reverse_mosi_data (
.in( mosi_data[MOSI_DATA_WIDTH-1:0] ),
.out( mosi_data_rev[MOSI_DATA_WIDTH-1:0] )
);
logic clk_pin_before_inversion; // inversion is optional, see CPOL parameter
logic [7:0] sequence_cntr = 0;
logic rd_nwr = 0; // buffering data direction
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
if( ~nrst ) begin
clk_pin_before_inversion <= CPOL;
ncs_pin <= 1'b1;
mosi_pin <= 1'b0;
oe_pin <= 1'b0;
sequence_cntr[7:0] <= 0;
rd_nwr <= 0;
mosi_data_buf[MOSI_DATA_WIDTH-1:0] <= 0;
miso_data_buf[MISO_DATA_WIDTH-1:0] <= 0;
end else begin
if( FREE_RUNNING_SPI_CLK ) begin
if ( spi_clk_rise ) begin
clk_pin_before_inversion <= 1'b1;
end
if( spi_clk_fall ) begin
clk_pin_before_inversion <= 1'b0;
end
end else begin // FREE_RUNNING_SPI_CLK = 0
if ( ~ncs_pin ) begin
if ( spi_clk_rise ) begin
clk_pin_before_inversion <= 1'b1;
end
if( spi_clk_fall ) begin
clk_pin_before_inversion <= 1'b0;
end
end else begin // ncs_pin = 1
clk_pin_before_inversion <= CPOL;
end
end // if( FREE_RUNNING_SPI_CLK )
// WRITE =======================================================================
// 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( spi_rd_cmd_rise ) begin
rd_nwr <= 1'b1;
end else begin
rd_nwr <= 1'b0;
end
// buffering mosi_data to avoid data change after shift_cmd issued
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
// clocking out data
if( sequence_cntr[7:0]>=WRITE_SEQ_START && sequence_cntr[7:0]<WRITE_SEQ_END ) begin
// we should omit this to start sequence on specific edge
if ( spi_clk_rise ) begin
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
if( spi_clk_fall ) begin
// changing mosi_pin
mosi_pin <= mosi_data_buf[0];
// shifting out data is alvays LSB first
mosi_data_buf[MOSI_DATA_WIDTH-1:0] <= {1'b0,mosi_data_buf[MOSI_DATA_WIDTH-1:1]};
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
end
// waiting for valid edge to switch direction
if( ~rd_nwr ) begin
// end of write transaction
// resetting shifter to default state
if( sequence_cntr[7:0]==WRITE_SEQ_END && spi_clk_fall ) begin
ncs_pin <= 1'b1;
mosi_pin <= 1'b0;
oe_pin <= 1'b0;
sequence_cntr[7:0] <= 0;
end
end else begin
if( sequence_cntr[7:0]==WRITE_SEQ_END && spi_clk_fall ) begin
//ncs_pin <= 1'b0;
mosi_pin <= 1'b0;
oe_pin <= 1'b0;
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
// READ ========================================================================
// clocking in data
if( sequence_cntr[7:0]>=READ_SEQ_START && sequence_cntr[7:0]<READ_SEQ_END ) begin
if ( spi_clk_rise ) begin
// shifting in data is alvays LSB first
miso_data_buf[MISO_DATA_WIDTH-1:0] <= {miso_pin,miso_data_buf[MOSI_DATA_WIDTH-1:1]};
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
// we should omit this to start sequence on specific edge
if( spi_clk_fall ) begin
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
end
// waiting for valid edge to end read transaction
if( sequence_cntr[7:0]==READ_SEQ_END && spi_clk_fall ) begin
ncs_pin <= 1'b1;
mosi_pin <= 1'b0;
oe_pin <= 1'b0;
sequence_cntr[7:0] <= 0;
end
end // if( ~rd_nwr )
end // if( nrst )
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
// 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);
end
endmodule
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/yuan_hp/basic_verilog.git
git@gitee.com:yuan_hp/basic_verilog.git
yuan_hp
basic_verilog
basic_verilog
master

搜索帮助