赞
踩
SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是一种同步串行接口技术,
是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输
优点:是支持全双工通信,通讯方式较为简单,且相对数据传输速率较快;
缺点:是没有指定的流控制,没有应答机制确认数据是否接收
SPI 通讯设备的通讯模式是主从通讯模式,通讯双方有主从之分(一主一从和一主多从)
(1) SCK (Serial Clock):时钟信号线,用于同步通讯数据。由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不同,两个设备之间通讯时,通讯速率受限于低速设备。
(2) MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,数据方向由主机到从机。
(3) MISO (Master Input,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,数据方向由从机到主机。
(4) CS(Chip Select):片选信号线,也称为 CS_N,以下用 CS_N 表示。当有多个 SPI 从设备与 SPI 主机相连时,设备的其它信号线 SCK、MOSI 及 MISO 同时并联到相同的 SPI总线上,即无论有多少个从设备,都共同使用这 3 条总线;而每个从设备都有独立的这一条 CS_N 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI协议中没有设备地址,它使用 CS_N 信号线来寻址,当主机要选择从设备时,把该从设备的 CS_N 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以 SPI 通讯以 CS_N 线置低电平为开始信号,以 CS_N 线被拉高作为结束信号。
SPI 通讯协议一共有四种通讯模式,模式 0、模式 1、模式 2 以及模式 3,这 4 种模式(0、3模式比较常用)
分别由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来定义,其中
CPOL 参数规定了空闲状态(CS_N 为高电平,设备未被选中)时 SCK 时钟信号的电平状态,(0:低 1:高)
CPHA 规定了数据采样是在 SCK 时钟的奇数边沿还是偶数边沿。(0:奇数边沿 1:偶数边沿)
模式 0:CPOL= 0,CPHA=0。空闲状态时 SCK 串行时钟为低电平;数据采样在 SCK时钟的奇数边沿,
本模式中,奇数边沿为上升沿;数据更新在 SCK 时钟的偶数边沿,本模式中,偶数边沿为下降沿。
模式 1:CPOL= 0,CPHA=1。空闲状态时 SCK 串行时钟为低电平;数据采样在 SCK时钟的偶数边沿,
本模式中,偶数边沿为下降沿;数据更新在 SCK 时钟的奇数边沿,本模式中,偶数边沿为上升沿。
模式 2:CPOL= 1,CPHA=0。空闲状态时 SCK 串行时钟为高电平;数据采样在 SCK时钟的奇数边沿,
本模式中,奇数边沿为下降沿;数据更新在 SCK 时钟的偶数边沿,本模式中,偶数边沿为上升沿。
模式 3:CPOL= 1,CPHA=1。空闲状态时 SCK 串行时钟为高电平;数据采样在 SCK时钟的偶数边沿,
本模式中,偶数边沿为上升沿;数据更新在 SCK 时钟的奇数边沿,本模式中,偶数边沿为下降沿。
红线对应的为数据采样点
MOSI 与 MISO 的信号只在 CS_N 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。
在图 46-6 中的标号①处,CS_N 信号线由高变低,是 SPI 通讯的起始信号。CS_N 是每个从机各自独占的信号线,当从机在自己的 CS_N 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。
在图中的标号⑥处,CS_N 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。
数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采图 46-6 中的 MSB 先行模式,先发送数据的最高位。
观察图中的②③④⑤标号处,MOSI 及 MISO 的数据在 SCK 的下降沿期间变化输出,在 SCK 的上升沿时被采样。即在 SCK 的上升沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数“0”。在其它时刻,数据无效,MOSI 及 MISO为下一次表示数据做准备。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。
高电平“1” 当前Flash还在工作(写状态寄存器、写入数据、擦除数据)
低电平“0” 当前Flash处于空闲的状态,可以处理指令。
高电平“1” 内部启用锁存设置,可以接收输入的指令
低电平“0” 除了写使能指令,其他指令一律不接收
①块保护(BP2、BP1、BP0)位是非易失性的。
②定义了需要保护数据的区域(只读),防止被指令擦除
③只能使用写状态寄存器指令修改
④当所有的BP(BP2、BP1、BP0)都为0的时候,才能执行BE(全擦除)指令
状态寄存器写保护位与写保护W(芯片的引脚)共同决定状态寄存器BP2、BP1、BP0为的读写属性。
写保护W引脚为低电平“0”的时候,SRWD才能决定BP的只读属性。
写保护W引脚为低电平“1”的时候,BP2、BP1、BP0为只读属性。
高电平“1” BP位只读
低电平“0” BP位可读可写
处于该状态下,说明flash正处于工作的状态(CS拉低、CS拉高之后一小段时间以内,都会处于该状态)。
处于该模式下,flash能够处理输入的指令,并且芯片的功耗也会降低。
写使能指令用于设置状态寄存器的WEL位,将该位置“1”,后续很多的指令都需要先写入该指令之后,才能被正确的识别。
写失能指令设置状态寄存器的WEL位,将该位置“0”
在以下的条件下,也会将WEL位置“0”
①上电
②执行WRDI指令完成
③执行WRSR(写状态寄存器)指令完成
④执行PP指令完成
⑤执行SE指令完成
⑥执行BE指令完成
可以直接发送该指令获取设备的信息(3个字节),不需要提前发送WREN指令。
发送完该指令之后,Flash立即进入Stand-by Power Mode,能够立即接收下一次指令。
状态寄存器可以在任何时候被读取,包括Flash处于Stand-by Power Mode,都可以被读取。
在发送一个指令之前,最好先读取状态寄存器的WIP(正在写入)位的值。
发送完该指令之后,Flash会一直发送状态寄存器的值 ,直到将片选信号拉高。
更新状态寄存器的值,发送该指令需要先发送WREN(写使能)指令。
在写入状态寄存器值的时候,依然可以读取状态寄存器WIP(正在写入)值,判断写入是否完成。
该指令写完之后,将WEL位值“0”。
发送该指令之后,后续紧跟3个字节的地址信息(扇区地址、页地址、字节地址),之后每次回传一次数据,就会在当前的地址基础上加1,直到片选CS信号拉高。
数据在时钟下降沿更新输出。输出数据的频率最大20MHz,所以时钟设置为20MHz。
理论上,该指令能够将整个Flash全部读完。
当Flash处于擦除、写入数据、写周期的状态时,任何的READ指令都会被拒绝。
写入该指令之前,需要先写入WREN指令,使能写的功能。
写入指令之后,需要写入3字节的地址信息,再传输需要存储的数据。
如果写入的数据个数大于了256(一页),那么就会覆盖掉最先存入的数据(在当前页循环存入)。
将内存中的值又由1变为0
执行完该指令之后,将状态寄存器的WEL置为“0”。
在写入指令之前,需要先写入WREN指令,使能写锁存器。
扇区擦除,将指定扇区的全部数据置“1”
完成该指令之后,状态寄存器WEL的值会置“0”
执行该指令的过程,可以读取状态寄存器的WIP的值,判断是否擦除完成
在执行指令之前,需要先执行WREN指令,将整个Flash的所有数据置为“1”
完成该指令之后,状态寄存器WEL的值会置“0”
执行该指令的过程,可以读取状态寄存器的WIP的值,判断是否擦除完成
module spi_master( input clk , input rst_n , input req , input [7:0] din , output [7:0] dout , output done , output cs_n , output mosi , input miso , output sclk ); //参数定义 localparam SCLK_PERIOD = 16, SCLK_FALL = 4 , SCLK_RISE = 12; //信号定义 reg [ 3:0] cnt_sclk ; wire add_cnt_sclk ; wire end_cnt_sclk ; reg [ 3:0] cnt_bit ; wire add_cnt_bit ; wire end_cnt_bit ; reg spi_sclk ; reg spi_mosi ; reg [7:0] rx_data ; //计数器 always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin cnt_sclk <= 0; end else if(add_cnt_sclk) begin if(end_cnt_sclk) cnt_sclk <= 0; else cnt_sclk <= cnt_sclk+1 ; end end assign add_cnt_sclk = (req); assign end_cnt_sclk = add_cnt_sclk && cnt_sclk == (SCLK_PERIOD)-1 ; always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin cnt_bit <= 0; end else if(add_cnt_bit) begin if(end_cnt_bit) cnt_bit <= 0; else cnt_bit <= cnt_bit+1 ; end end assign add_cnt_bit = (end_cnt_sclk); assign end_cnt_bit = add_cnt_bit && cnt_bit == (8)-1 ; //spi_sclk 模式3 always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin spi_sclk <= 1'b1; end else if(cnt_sclk == SCLK_FALL-1)begin spi_sclk <= 1'b0; end else if(cnt_sclk == SCLK_RISE-1)begin spi_sclk <= 1'b1; end end //发送数据 always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin spi_mosi <= 1'b0; end else if(cnt_sclk == SCLK_FALL-1)begin spi_mosi <= din[7-cnt_bit]; end end //接收数据 always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin rx_data <= 0; end else if(cnt_sclk == SCLK_RISE-1)begin rx_data[7-cnt_bit] <= miso; end end //输出 assign sclk = spi_sclk; assign mosi = spi_mosi; assign cs_n = ~req; assign done = end_cnt_bit; assign dout = rx_data; endmodule
module flash_read( input clk , input rst_n , //key input rd_id , input rd_data , input [23:0] rd_addr ,//flash读地址 //spi_master output trans_req , output [7:0] tx_dout , input [7:0] rx_din , input trans_done , //output output [47:0] dout , output [5:0] dout_mask , output dout_vld ); /********* 工程注释 **************** M25P16 Flash读控制器,实现读数据和读存储器的ID。 ********** 注释结束 ****************/ //状态机参数定义 localparam IDLE = 4'b0001, RDID = 4'b0010,//读器件ID RDDA = 4'b0100,//读数据字节 DONE = 4'b1000; //Flash命令参数定义 localparam CMD_RDID = 8'h9F, CMD_RDDA = 8'h03; //信号定义 reg [3:0] state_c ; reg [3:0] state_n ; reg [3:0] cnt_byte ; wire add_cnt_byte; wire end_cnt_byte; reg [3:0] byte_num ; reg [7:0] tx_data ; reg tx_req ; reg flag ;//读数据、读ID标志 wire idle2rdid ; wire rdid2done ; wire idle2rdda ; wire rdda2done ; wire done2idle ; reg [31:0] rx_data /* synthesis keep */;//串并转换寄存器 reg [47:0] data ; reg [5:0] data_mask ; reg data_vld ; //状态机 always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin state_c <= IDLE ; end else begin state_c <= state_n; end end always @(*) begin case(state_c) IDLE :begin if(idle2rdid) state_n = RDID ; else if(idle2rdda) state_n = RDDA ; else state_n = state_c ; end RDID :begin if(rdid2done) state_n = DONE ; else state_n = state_c ; end RDDA :begin if(rdda2done) state_n = DONE ; else state_n = state_c ; end DONE :begin if(done2idle) state_n = IDLE ; else state_n = state_c ; end default : state_n = IDLE ; endcase end assign idle2rdid = state_c==IDLE && (rd_id ); assign rdid2done = state_c==RDID && (end_cnt_byte); assign idle2rdda = state_c==IDLE && (rd_data); assign rdda2done = state_c==RDDA && (end_cnt_byte); assign done2idle = state_c==DONE && (1'b1); always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin cnt_byte <= 0; end else if(add_cnt_byte) begin if(end_cnt_byte) cnt_byte <= 0; else cnt_byte <= cnt_byte+1 ; end end assign add_cnt_byte = (state_c != IDLE && trans_done); assign end_cnt_byte = add_cnt_byte && cnt_byte == (byte_num)-1 ; always @(*)begin if(state_c == RDID) byte_num = 4; else byte_num = 8; //至少5B CMD + 3 ADDR + X DATA end //输出 always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin tx_req <= 1'b0; end else if(idle2rdid | idle2rdda)begin tx_req <= 1'b1; end else if(rdid2done | rdda2done)begin tx_req <= 1'b0; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin tx_data <= 0; end else if(state_c == RDID)begin case(cnt_byte) 0:tx_data <= CMD_RDID; default:tx_data <= 0; endcase end else if(state_c == RDDA)begin case(cnt_byte) 0:tx_data <= CMD_RDDA; 1:tx_data <= rd_addr[23:16]; 2:tx_data <= rd_addr[15:8]; 3:tx_data <= rd_addr[7:0]; default:tx_data <= 0; endcase end end //rx_data always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin rx_data <= 0; end else if(state_c == RDID && trans_done)begin rx_data <= {rx_data[23:0],rx_din}; end else if(state_c == RDDA && trans_done)begin rx_data <= {rx_data[23:0],rx_din}; end end //data always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin data <= 0; end else if(state_c == DONE && ~flag)begin //读ID data <= {4'd0,rx_data[23:20],4'd0,rx_data[19:16], //2 0 4'd0,rx_data[15:12],4'd0,rx_data[11:8], //2 0 4'd0,rx_data[7:4],4'd0,rx_data[3:0]}; //1 5 end else if(state_c == DONE && flag)begin //读数据 data <= {"R","D",16'd0,4'd0,rx_data[7:4],4'd0,rx_data[3:0]}; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin data_mask <= 0; end else if(state_c == DONE && ~flag)begin data_mask <= 6'b00_0000; end else if(state_c == DONE && flag)begin data_mask <= 6'b00_1100; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin data_vld <= 0; end else begin data_vld <= state_c == DONE; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin flag <= 1'b0; end else if(idle2rdda)begin flag <= 1'b1; end else if(idle2rdid)begin flag <= 1'b0; end end //输出 assign tx_dout =tx_data; assign trans_req = tx_req; assign dout = data; assign dout_mask = data_mask; assign dout_vld = data_vld; endmodule
module flash_write( input clk , input rst_n , //key input write , input [ 7:0] wr_data ,//写入的数据 input [23:0] wr_addr ,//flash写地址 //spi_master output trans_req , output [7:0] tx_dout , input [7:0] rx_din , input trans_done , //output output [47:0] dout , output [5:0] dout_mask , output dout_vld ); //参数定义 //主状态机状态参数 localparam M_IDLE = 5'b0_0001, M_WREN = 5'b0_0010,//主机发写使能序列 M_SE = 5'b0_0100,//主机发扇区擦除序列 M_RDSR = 5'b0_1000,//主机发读状态寄存器序列 M_PP = 5'b1_0000;//主机发页编程序列 //从状态机状态参数 localparam S_IDLE = 5'b0_0001, S_CMD = 5'b0_0010,//发送命令 S_ADDR = 5'b0_0100,//发送地址 S_DATA = 5'b0_1000,//发送数据、接收数据 S_DELA = 5'b1_0000;//延时 拉高片选信号 localparam CMD_WREN = 8'h06,//写使能命令 CMD_SE = 8'hD8,//擦除命令 CMD_RDSR = 8'h05,//读状态寄存器命令 CMD_PP = 8'h02;//页编程命令 parameter TIME_DELAY = 16,//发完WREN、SE、PP序列 拉高片选 TIME_SE = 150_000_000,//扇区擦除3s TIME_PP = 25_000,//页编程5ms TIME_RDSR = 2000;//读状态寄存器 最大2000次 //信号定义 reg [4:0] m_state_c ; reg [4:0] m_state_n ; reg [4:0] s_state_c ; reg [4:0] s_state_n ; reg [3:0] cnt0 ; wire add_cnt0 ; wire end_cnt0 ; reg [3:0] xx ; reg [31:0] cnt1 ; wire add_cnt1 ; wire end_cnt1 ; reg [31:0] yy ; reg rdsr_wip ; reg rdsr_wel ; reg [2:0] flag ; reg [7:0] tx_data ; reg tx_req ; reg [47:0] data ; reg [5:0] data_mask ; reg data_vld ; wire m_idle2m_wren ; wire m_wren2m_se ; wire m_se2m_rdsr ; wire m_rdsr2m_wren ; wire m_rdsr2m_pp ; wire m_wren2m_pp ; wire m_rdsr2m_idle ; wire m_pp2m_rdsr ; wire s_idle2s_cmd ; wire s_cmd2s_addr ; wire s_cmd2s_data ; wire s_cmd2s_dela ; wire s_addr2s_data ; wire s_addr2s_dela ; wire s_data2s_dela ; wire s_dela2s_idle ; //状态机设计 always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin m_state_c <= M_IDLE ; end else begin m_state_c <= m_state_n; end end always @(*) begin case(m_state_c) M_IDLE :begin if(m_idle2m_wren) m_state_n = M_WREN ; else m_state_n = m_state_c ; end M_WREN :begin if(m_wren2m_se) m_state_n = M_SE ; else if(m_wren2m_pp) m_state_n = M_PP ; else m_state_n = m_state_c ; end M_SE :begin if(m_se2m_rdsr) m_state_n = M_RDSR ; else m_state_n = m_state_c ; end M_RDSR :begin if(m_rdsr2m_wren) m_state_n = M_WREN ; else if(m_rdsr2m_pp) m_state_n = M_PP ; else if(m_rdsr2m_idle) m_state_n = M_IDLE ; else m_state_n = m_state_c ; end M_PP :begin if(m_pp2m_rdsr) m_state_n = M_RDSR ; else m_state_n = m_state_c ; end default : m_state_n = M_IDLE ; endcase end assign m_idle2m_wren = m_state_c==M_IDLE && (write); assign m_wren2m_se = m_state_c==M_WREN && (s_dela2s_idle && flag[0]); assign m_se2m_rdsr = m_state_c==M_SE && (s_dela2s_idle); assign m_rdsr2m_wren = m_state_c==M_RDSR && (s_dela2s_idle && ~rdsr_wel && ~rdsr_wip && flag[1]); assign m_rdsr2m_pp = m_state_c==M_RDSR && (s_dela2s_idle && rdsr_wel && ~rdsr_wip && flag[1]); assign m_wren2m_pp = m_state_c==M_WREN && (s_dela2s_idle && flag[1]); assign m_rdsr2m_idle = m_state_c==M_RDSR && (s_dela2s_idle && flag[2]); assign m_pp2m_rdsr = m_state_c==M_PP && (s_dela2s_idle); //从状态机设计 always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin s_state_c <= S_IDLE ; end else begin s_state_c <= s_state_n; end end always @(*) begin case(s_state_c) S_IDLE :begin if(s_idle2s_cmd) s_state_n = S_CMD ; else s_state_n = s_state_c ; end S_CMD :begin if(s_cmd2s_addr) s_state_n = S_ADDR ; else if(s_cmd2s_data) s_state_n = S_DATA ; else if(s_cmd2s_dela) s_state_n = S_DELA ; else s_state_n = s_state_c ; end S_ADDR :begin if(s_addr2s_data) s_state_n = S_DATA ; else if(s_addr2s_dela) s_state_n = S_DELA ; else s_state_n = s_state_c ; end S_DATA :begin if(s_data2s_dela) s_state_n = S_DELA ; else s_state_n = s_state_c ; end S_DELA :begin if(s_dela2s_idle) s_state_n = S_IDLE ; else s_state_n = s_state_c ; end default : s_state_n = S_IDLE ; endcase end assign s_idle2s_cmd = s_state_c==S_IDLE && (m_state_c != M_IDLE); assign s_cmd2s_addr = s_state_c==S_CMD && (trans_done && (m_state_c == M_SE || m_state_c == M_PP)); assign s_cmd2s_data = s_state_c==S_CMD && (trans_done && m_state_c == M_RDSR); assign s_cmd2s_dela = s_state_c==S_CMD && (trans_done && m_state_c == M_WREN); assign s_addr2s_data = s_state_c==S_ADDR && (end_cnt0 && (m_state_c == M_RDSR || m_state_c == M_PP)); assign s_addr2s_dela = s_state_c==S_ADDR && (end_cnt0 && m_state_c == M_SE); assign s_data2s_dela = s_state_c==S_DATA && (end_cnt0 && m_state_c == M_PP || m_state_c == M_RDSR && end_cnt1); assign s_dela2s_idle = s_state_c==S_DELA && (end_cnt1); //计数器设计 always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin cnt0 <= 0; end else if(add_cnt0) begin if(end_cnt0) cnt0 <= 0; else cnt0 <= cnt0+1 ; end end assign add_cnt0 = (m_state_c != M_RDSR && trans_done); assign end_cnt0 = add_cnt0 && cnt0 == (xx)-1 ; always @(*)begin if(s_state_c == S_CMD) //发命令1字节 xx = 1; else if(s_state_c == S_ADDR) //发地址3字节 xx = 3; else /*if(s_state_c == S_DATA)*/ //发数据1字节 xx = 4; end always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin cnt1 <= 0; end else if(add_cnt1) begin if(end_cnt1) cnt1 <= 0; else cnt1 <= cnt1+1 ; end end assign add_cnt1 = (s_state_c == S_DELA || m_state_c == M_RDSR && s_state_c == S_DATA && trans_done); assign end_cnt1 = add_cnt1 && (cnt1 == (yy)-1 || trans_done && ~rdsr_wip); always @(*)begin if((m_state_c == M_WREN || m_state_c == M_RDSR) && s_state_c == S_DELA) //发完写使能延时 yy = TIME_DELAY; else if(m_state_c == M_SE)//发完擦除延时 yy = TIME_SE; else if(m_state_c == M_PP)//发完页编程延时 yy = TIME_PP; else if(m_state_c == M_RDSR && s_state_c == S_DATA)//读状态寄存器值 yy 次 yy = TIME_RDSR; else yy = TIME_DELAY; end //rdsr_wel rdsr_wip always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin rdsr_wel <= 1'b0; rdsr_wip <= 1'b0; end else if(m_state_c == M_RDSR && s_state_c == S_DATA && trans_done)begin rdsr_wel <= rx_din[1]; rdsr_wip <= rx_din[0]; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin flag <= 3'b000; end else if(m_idle2m_wren)begin flag <= 3'b001; end else if(m_se2m_rdsr)begin flag <= 3'b010; end else if(m_pp2m_rdsr)begin flag <= 3'b100; end end //输出 always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin tx_data <= 0; end else if(m_state_c == M_WREN)begin tx_data <= CMD_WREN; end else if(m_state_c == M_SE)begin case(s_state_c) S_CMD :tx_data <= CMD_SE; S_ADDR:tx_data <= wr_addr[23-cnt0*8 -:8]; default:tx_data <= 0; endcase end else if(m_state_c == M_RDSR)begin //在读状态寄存器时,可以发一次命令,也可以连续发命令 tx_data <= CMD_RDSR; end else if(m_state_c == M_PP)begin case(s_state_c) S_CMD :tx_data <= CMD_PP; S_ADDR:tx_data <= wr_addr[23-cnt0*8 -:8]; S_DATA:tx_data <= wr_data + cnt0; default:tx_data <= 0; endcase end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin tx_req <= 1'b0; end else if(s_idle2s_cmd)begin tx_req <= 1'b1; end else if(s_cmd2s_dela | s_addr2s_dela | s_data2s_dela)begin tx_req <= 1'b0; end end //data data_mask always @(posedge clk or negedge rst_n)begin if(!rst_n)begin data <= 0; data_mask <= 0; end else if(m_rdsr2m_idle & ~rdsr_wip)begin //PP completed data <= {"P","P",16'd0,4'd0,wr_data[7:4],4'd0,wr_data[3:0]}; data_mask <= 6'b001100; end else if(m_rdsr2m_idle & rdsr_wip)begin //PP failed data <= {"P","P",8'd0,"ERR"}; data_mask <= 6'b001000; end end //data_vld , always @(posedge clk or negedge rst_n)begin if(!rst_n)begin data_vld <= 0; end else begin data_vld <= m_rdsr2m_idle; end end //输出 assign tx_dout = tx_data; assign trans_req = tx_req; assign dout_vld = data_vld; assign dout_mask = data_mask; assign dout = data; endmodule
module flash_ctrl( input clk , input rst_n , input [2:0] key , input [ 7:0] wr_din ,//写入的数据 input [23:0] rw_addr ,//flash读写地址 output trans_req , output [7:0] tx_dout , input [7:0] rx_din , input trans_done , output [47:0] dout , output [5:0] dout_mask , output dout_vld ); //信号定义 wire wr_req ; wire [7:0] wr_dout ; wire [47:0] wr_data ; wire [5:0] wr_data_mask ; wire wr_data_vld ; wire rd_req ; wire [7:0] rd_dout ; wire [47:0] rd_data ; wire [5:0] rd_data_mask ; wire rd_data_vld ; //模块例化 flash_write u_write( /*input */.clk (clk ), /*input */.rst_n (rst_n ), /*input */.write (key[2] ), /*input [ 7:0] */.wr_data (wr_din ),//写入的数据 /*input [23:0] */.wr_addr (rw_addr ),//flash写地址 /*output */.trans_req (wr_req ), /*output [7:0] */.tx_dout (wr_dout ), /*input [7:0] */.rx_din (rx_din ), /*input */.trans_done (trans_done ), /*output [47:0] */.dout (wr_data ), /*output [5:0] */.dout_mask (wr_data_mask ), /*output */.dout_vld (wr_data_vld ) ); flash_read u_read( /*input */.clk (clk ), /*input */.rst_n (rst_n ), /*input */.rd_id (key[0] ), /*input */.rd_data (key[1] ), /*input [23:0] */.rd_addr (rw_addr ),//flash读地址 /*output */.trans_req (rd_req ), /*output [7:0] */.tx_dout (rd_dout ), /*input [7:0] */.rx_din (rx_din ), /*input */.trans_done (trans_done ), /*output [47:0] */.dout (rd_data ), /*output [5:0] */.dout_mask (rd_data_mask ), /*output */.dout_vld (rd_data_vld ) ); assign trans_req = rd_req | wr_req; assign tx_dout = {8{wr_req}} & wr_dout | {8{rd_req}} & rd_dout; assign dout_vld = wr_data_vld | rd_data_vld; assign dout = {48{wr_data_vld}} & wr_data | {48{rd_data_vld}} & rd_data; assign dout_mask = {6{wr_data_vld}} & wr_data_mask | {6{rd_data_vld}} & rd_data_mask; endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。