FPGA进阶(1):基于SPI协议的Flash驱动控制(一)+https://developer.aliyun.com/article/1556611
3. 数据读操作
key_filter
`timescale 1ns/1ns module key_filter #( parameter CNT_MAX = 20'd999_999 //计数器计数最大值 ) ( input wire sys_clk , //系统时钟50Mhz input wire sys_rst_n , //全局复位 input wire key_in , //按键输入信号 output reg key_flag //key_flag为1时表示消抖后检测到按键被按下 //key_flag为0时表示没有检测到按键被按下 ); //reg define reg [19:0] cnt_20ms ; //计数器 //cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_20ms <= 20'b0; else if(key_in == 1'b1) cnt_20ms <= 20'b0; else if(cnt_20ms == CNT_MAX && key_in == 1'b0) cnt_20ms <= cnt_20ms; else cnt_20ms <= cnt_20ms + 1'b1; //key_flag:当计数满20ms后产生按键有效标志位 //且key_flag在999_999时拉高,维持一个时钟的高电平 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) key_flag <= 1'b0; else if(cnt_20ms == CNT_MAX - 1'b1) key_flag <= 1'b1; else key_flag <= 1'b0; endmodule
uart_tx
`timescale 1ns/1ns module uart_tx #( parameter UART_BPS = 'd9600, //串口波特率 parameter CLK_FREQ = 'd50_000_000 //时钟频率 ) ( input wire sys_clk , //系统时钟50MHz input wire sys_rst_n , //全局复位 input wire [7:0] pi_data , //模块输入的8bit数据 input wire pi_flag , //并行数据有效标志信号 output reg tx //串转并后的1bit数据 ); //localparam define localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ; //reg define reg [12:0] baud_cnt; reg bit_flag; reg [3:0] bit_cnt ; reg work_en ; //work_en:接收数据工作使能信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) work_en <= 1'b0; else if(pi_flag == 1'b1) work_en <= 1'b1; else if((bit_flag == 1'b1) && (bit_cnt == 4'd9)) work_en <= 1'b0; //baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) baud_cnt <= 13'b0; else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) baud_cnt <= 13'b0; else if(work_en == 1'b1) baud_cnt <= baud_cnt + 1'b1; //bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_flag <= 1'b0; else if(baud_cnt == 13'd1) bit_flag <= 1'b1; else bit_flag <= 1'b0; //bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_cnt <= 4'b0; else if((bit_flag == 1'b1) && (bit_cnt == 4'd9)) bit_cnt <= 4'b0; else if((bit_flag == 1'b1) && (work_en == 1'b1)) bit_cnt <= bit_cnt + 1'b1; //tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) tx <= 1'b1; //空闲状态时为高电平 else if(bit_flag == 1'b1) case(bit_cnt) 0 : tx <= 1'b0; 1 : tx <= pi_data[0]; 2 : tx <= pi_data[1]; 3 : tx <= pi_data[2]; 4 : tx <= pi_data[3]; 5 : tx <= pi_data[4]; 6 : tx <= pi_data[5]; 7 : tx <= pi_data[6]; 8 : tx <= pi_data[7]; 9 : tx <= 1'b1; default : tx <= 1'b1; endcase endmodule
flash_read_ctrl
`timescale 1ns/1ns module flash_read_ctrl( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低电平有效 input wire key , //按键输入信号 input wire miso , //读出flash数据 output reg sck , //串行时钟 output reg cs_n , //片选信号 output reg mosi , //主输出从输入数据 output reg tx_flag , //输出数据标志信号 output wire [7:0] tx_data //输出数据 ); //parameter define parameter IDLE = 3'b001 , //初始状态 READ = 3'b010 , //数据读状态 SEND = 3'b100 ; //数据发送状态 parameter READ_INST = 8'b0000_0011; //读指令 parameter NUM_DATA = 16'd100 ; //读出数据个数 parameter SECTOR_ADDR = 8'b0000_0000, //扇区地址 PAGE_ADDR = 8'b0000_0100, //页地址 BYTE_ADDR = 8'b0010_0101; //字节地址 parameter CNT_WAIT_MAX= 16'd6_00_00 ; //wire define wire [7:0] fifo_data_num ; //fifo内数据个数 //reg define reg [4:0] cnt_clk ; //系统时钟计数器 reg [2:0] state ; //状态机状态 reg [15:0] cnt_byte ; //字节计数器 reg [1:0] cnt_sck ; //串行时钟计数器 reg [2:0] cnt_bit ; //比特计数器 reg miso_flag ; //miso提取标志信号 reg [7:0] data ; //拼接数据 reg po_flag_reg ; //输出数据标志信号 reg po_flag ; //输出数据 reg [7:0] po_data ; //输出数据 reg fifo_read_valid ; //fifo读有效信号 reg [15:0] cnt_wait ; //等待计数器 reg fifo_read_en ; //fifo读使能 reg [7:0] read_data_num ; //读出fifo数据个数 //cnt_clk:系统时钟计数器,用以记录单个字节 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_clk <= 5'd0; else if(state == READ) cnt_clk <= cnt_clk + 1'b1; //cnt_byte:记录输出字节个数和等待时间 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_byte <= 16'd0; else if((cnt_clk == 5'd31) && (cnt_byte == NUM_DATA + 16'd3)) cnt_byte <= 16'd0; else if(cnt_clk == 5'd31) cnt_byte <= cnt_byte + 1'b1; //cnt_sck:串行时钟计数器,用以生成串行时钟 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_sck <= 2'd0; else if(state == READ) cnt_sck <= cnt_sck + 1'b1; //cs_n:片选信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cs_n <= 1'b1; else if(key == 1'b1) cs_n <= 1'b0; else if((cnt_byte == NUM_DATA + 16'd3) && (cnt_clk == 5'd31) && (state == READ)) cs_n <= 1'b1; //sck:输出串行时钟 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) sck <= 1'b0; else if(cnt_sck == 2'd0) sck <= 1'b0; else if(cnt_sck == 2'd2) sck <= 1'b1; //cnt_bit:高低位对调,控制mosi输出 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_bit <= 3'd0; else if(cnt_sck == 2'd2) cnt_bit <= cnt_bit + 1'b1; //state:两段式状态机第一段,状态跳转 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) state <= IDLE; else case(state) IDLE: if(key == 1'b1) state <= READ; READ: if((cnt_byte == NUM_DATA + 16'd3) && (cnt_clk == 5'd31)) state <= SEND; SEND: if((read_data_num == NUM_DATA) && ((cnt_wait == (CNT_WAIT_MAX - 1'b1)))) state <= IDLE; default: state <= IDLE; endcase //mosi:两段式状态机第二段,逻辑输出 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) mosi <= 1'b0; else if((state == READ) && (cnt_byte>= 16'd4)) mosi <= 1'b0; else if((state == READ) && (cnt_byte == 16'd0) && (cnt_sck == 2'd0)) mosi <= READ_INST[7 - cnt_bit]; //读指令 else if((state == READ) && (cnt_byte == 16'd1) && (cnt_sck == 2'd0)) mosi <= SECTOR_ADDR[7 - cnt_bit]; //扇区地址 else if((state == READ) && (cnt_byte == 16'd2) && (cnt_sck == 2'd0)) mosi <= PAGE_ADDR[7 - cnt_bit]; //页地址 else if((state == READ) && (cnt_byte == 16'd3) && (cnt_sck == 2'd0)) mosi <= BYTE_ADDR[7 - cnt_bit]; //字节地址 //miso_flag:miso提取标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) miso_flag <= 1'b0; else if((cnt_byte >= 16'd4) && (cnt_sck == 2'd1)) miso_flag <= 1'b1; else miso_flag <= 1'b0; //data:拼接数据 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data <= 8'd0; else if(miso_flag == 1'b1) data <= {data[6:0],miso}; //po_flag_reg:输出数据标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) po_flag_reg <= 1'b0; else if((cnt_bit == 3'd7) && (miso_flag == 1'b1)) po_flag_reg <= 1'b1; else po_flag_reg <= 1'b0; //po_flag:输出数据标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) po_flag <= 1'b0; else po_flag <= po_flag_reg; //po_data:输出数据 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) po_data <= 8'd0; else if(po_flag_reg == 1'b1) po_data <= data; else po_data <= po_data; //fifo_read_valid:fifo读有效信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) fifo_read_valid <= 1'b0; else if((read_data_num == NUM_DATA) && ((cnt_wait == (CNT_WAIT_MAX - 1'b1)))) fifo_read_valid <= 1'b0; else if(fifo_data_num == NUM_DATA) fifo_read_valid <= 1'b1; //cnt_wait:两数据读取时间间隔 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_wait <= 16'd0; else if(fifo_read_valid == 1'b0) cnt_wait <= 16'd0; else if(cnt_wait == (CNT_WAIT_MAX - 1'b1)) cnt_wait <= 16'd0; else if(fifo_read_valid == 1'b1) cnt_wait <= cnt_wait + 1'b1; //fifo_read_en:fifo读使能信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) fifo_read_en <= 1'b0; else if((cnt_wait == (CNT_WAIT_MAX - 1'b1)) && (read_data_num < NUM_DATA)) fifo_read_en <= 1'b1; else fifo_read_en <= 1'b0; //read_data_num:自fifo中读出数据个数计数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) read_data_num <= 8'd0; else if(fifo_read_valid == 1'b0) read_data_num <= 8'd0; else if(fifo_read_en == 1'b1) read_data_num <= read_data_num + 1'b1; //tx_flag always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) tx_flag <= 1'b0; else tx_flag <= fifo_read_en; //-------------fifo_data_inst-------------- fifo_data fifo_data_inst( .clock (sys_clk ), //时钟信号 .data (po_data ), //写数据,8bit .wrreq (po_flag ), //写请求 .rdreq (fifo_read_en ), //读请求 .q (tx_data ), //数据读出,8bit .usedw (fifo_data_num) //fifo内数据个数 ); endmodule
spi_flash_read
`timescale 1ns/1ns module spi_flash_read( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低电平有效 input wire pi_key , //按键输入信号 input wire miso , //读出flash数据 output wire cs_n , //片选信号 output wire sck , //串行时钟 output wire mosi , //主输出从输入数据 output wire tx ); //parameter define parameter CNT_MAX = 20'd999_999 ; //计数器计数最大值 parameter UART_BPS = 14'd9600 , //比特率 CLK_FREQ = 26'd50_000_000 ; //时钟频率 //wire define wire po_key ; //消抖处理后的按键信号 wire tx_flag ; //输入串口发送模块数据标志信号 wire [7:0] tx_data ; //输入串口发送模块数据 //------------- key_filter_inst ------------- key_filter #( .CNT_MAX (CNT_MAX ) //计数器计数最大值 ) key_filter_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .key_in (pi_key ), //按键输入信号 .key_flag (po_key ) //消抖后信号 ); //-------------flash_read_ctrl_inst------------- flash_read_ctrl flash_read_ctrl_inst( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .key (po_key ), //按键输入信号 .miso (miso ), //读出flash数据 .sck (sck ), //片选信号 .cs_n (cs_n ), //串行时钟 .mosi (mosi ), //主输出从输入数据 .tx_flag (tx_flag ), //输出数据标志信号 .tx_data (tx_data ) //输出数据 ); //-------------uart_tx_inst------------- uart_tx #( .UART_BPS (UART_BPS ), //串口波特率 .CLK_FREQ (CLK_FREQ ) //时钟频率 ) uart_tx_inst( .sys_clk (sys_clk ), //系统时钟50Mhz .sys_rst_n (sys_rst_n), //全局复位 .pi_data (tx_data ), //并行数据 .pi_flag (tx_flag ), //并行数据有效标志信号 .tx (tx ) //串口发送数据 ); endmodule
tb_spi_flash_read
`timescale 1ns/1ns module tb_spi_flash_read(); //wire define wire cs_n; wire sck ; wire mosi; wire miso; wire tx ; //reg define reg clk ; reg rst_n ; reg key ; //时钟、复位信号、模拟按键信号 initial begin clk = 0; rst_n <= 0; key <= 0; #100 rst_n <= 1; #1000 key <= 1; #20 key <= 0; end always #10 clk <= ~clk; defparam memory.mem_access.initfile = "initM25P16_test.txt"; defparam spi_flash_read_inst.flash_read_ctrl_inst.CNT_WAIT_MAX = 1000; defparam spi_flash_read_inst.uart_tx_inst.CLK_FREQ = 100000; //------------- spi_flash_read ------------- spi_flash_read spi_flash_read_inst( .sys_clk (clk ), //input sys_clk .sys_rst_n (rst_n ), //input sys_rst .pi_key (key ), //input key .miso (miso ), .sck (sck ), //output sck .cs_n (cs_n ), //output cs_n .mosi (mosi ), //output mosi .tx (tx ) ); //------------- memory ------------- m25p16 memory ( .c (sck ), .data_in (mosi ), .s (cs_n ), .w (1'b1 ), .hold (1'b1 ), .data_out (miso ) ); endmodule
4. 数据页写操作
两种写入方式:页写,连续写
key_filter
`timescale 1ns/1ns module key_filter #( parameter CNT_MAX = 20'd999_999 //计数器计数最大值 ) ( input wire sys_clk , //系统时钟50Mhz input wire sys_rst_n , //全局复位 input wire key_in , //按键输入信号 output reg key_flag //key_flag为1时表示消抖后检测到按键被按下 //key_flag为0时表示没有检测到按键被按下 ); //reg define reg [19:0] cnt_20ms ; //计数器 //cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_20ms <= 20'b0; else if(key_in == 1'b1) cnt_20ms <= 20'b0; else if(cnt_20ms == CNT_MAX && key_in == 1'b0) cnt_20ms <= cnt_20ms; else cnt_20ms <= cnt_20ms + 1'b1; //key_flag:当计数满20ms后产生按键有效标志位 //且key_flag在999_999时拉高,维持一个时钟的高电平 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) key_flag <= 1'b0; else if(cnt_20ms == CNT_MAX - 1'b1) key_flag <= 1'b1; else key_flag <= 1'b0; endmodule
flash_pp_ctrl
`timescale 1ns/1ns module flash_pp_ctrl( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低电平有效 input wire key , //按键输入信号 output reg cs_n , //片选信号 output reg sck , //串行时钟 output reg mosi //主输出从输入数据 ); //parameter define parameter IDLE = 4'b0001 , //初始状态 WR_EN = 4'b0010 , //写状态 DELAY = 4'b0100 , //等待状态 PP = 4'b1000 ; //页写状态 parameter WR_EN_INST = 8'b0000_0110, //写使能指令 PP_INST = 8'b0000_0010; //页写指令 parameter SECTOR_ADDR = 8'b0000_0000, //扇区地址 PAGE_ADDR = 8'b0000_0100, //页地址 BYTE_ADDR = 8'b0010_0101; //字节地址 parameter NUM_DATA = 8'd100 ; //页写数据个数(0-99) //reg define reg [7:0] cnt_byte ; //字节计数器 reg [3:0] state ; //状态机状态 reg [4:0] cnt_clk ; //系统时钟计数器 reg [1:0] cnt_sck ; //串行时钟计数器 reg [2:0] cnt_bit ; //比特计数器 reg [7:0] data ; //页写入数据 //cnt_clk:系统时钟计数器,用以记录单个字节 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_clk <= 5'd0; else if(state != IDLE) cnt_clk <= cnt_clk + 1'b1; //cnt_byte:记录输出字节个数和等待时间 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_byte <= 8'd0; else if((cnt_clk == 5'd31) && (cnt_byte == NUM_DATA + 8'd9)) cnt_byte <= 8'd0; else if(cnt_clk == 5'd31) cnt_byte <= cnt_byte + 1'b1; //cnt_sck:串行时钟计数器,用以生成串行时钟 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_sck <= 2'd0; else if((state == WR_EN) && (cnt_byte == 8'd1)) cnt_sck <= cnt_sck + 1'b1; else if((state == PP) && (cnt_byte >= 8'd5) && (cnt_byte <= NUM_DATA + 8'd9 - 1'b1)) cnt_sck <= cnt_sck + 1'b1; //cs_n:片选信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cs_n <= 1'b1; else if(key == 1'b1) cs_n <= 1'b0; else if((cnt_byte == 8'd2) && (cnt_clk == 5'd31) && (state == WR_EN)) cs_n <= 1'b1; else if((cnt_byte == 8'd3) && (cnt_clk == 5'd31) && (state == DELAY)) cs_n <= 1'b0; else if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31) && (state == PP)) cs_n <= 1'b1; //sck:输出串行时钟 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) sck <= 1'b0; else if(cnt_sck == 2'd0) sck <= 1'b0; else if(cnt_sck == 2'd2) sck <= 1'b1; //cnt_bit:高低位对调,控制mosi输出 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_bit <= 3'd0; else if(cnt_sck == 2'd2) cnt_bit <= cnt_bit + 1'b1; //data:页写入数据 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data <= 8'd0; else if((cnt_clk == 5'd31) && ((cnt_byte >= 8'd9) && (cnt_byte < NUM_DATA + 8'd9 - 1'b1))) data <= data + 1'b1; //state:两段式状态机第一段,状态跳转 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) state <= IDLE; else case(state) IDLE: if(key == 1'b1) state <= WR_EN; WR_EN: if((cnt_byte == 8'd2) && (cnt_clk == 5'd31)) state <= DELAY; DELAY: if((cnt_byte == 8'd3) && (cnt_clk == 5'd31)) state <= PP; PP: if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31)) state <= IDLE; default: state <= IDLE; endcase //mosi:两段式状态机第二段,逻辑输出 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) mosi <= 1'b0; else if((state == WR_EN) && (cnt_byte== 8'd2)) mosi <= 1'b0; else if((state == PP) && (cnt_byte == NUM_DATA + 8'd9)) mosi <= 1'b0; else if((state == WR_EN) && (cnt_byte == 8'd1) && (cnt_sck == 5'd0)) mosi <= WR_EN_INST[7 - cnt_bit]; //写使能指令 else if((state == PP) && (cnt_byte == 8'd5) && (cnt_sck == 5'd0)) mosi <= PP_INST[7 - cnt_bit]; //页写指令 else if((state == PP) && (cnt_byte == 8'd6) && (cnt_sck == 5'd0)) mosi <= SECTOR_ADDR[7 - cnt_bit]; //扇区地址 else if((state == PP) && (cnt_byte == 8'd7) && (cnt_sck == 5'd0)) mosi <= PAGE_ADDR[7 - cnt_bit]; //页地址 else if((state == PP) && (cnt_byte == 8'd8) && (cnt_sck == 5'd0)) mosi <= BYTE_ADDR[7 - cnt_bit]; //字节地址 else if((state == PP) && ((cnt_byte >= 8'd9) && (cnt_byte <= NUM_DATA + 8'd9 - 1'b1)) && (cnt_sck == 5'd0)) mosi <= data[7 - cnt_bit]; //页写入数据 endmodule
spi_flash_pp
`timescale 1ns/1ns module spi_flash_pp ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低电平有效 input wire pi_key , //按键输入信号 output wire cs_n , //片选信号 output wire sck , //串行时钟 output wire mosi //主输出从输入数据 ); //parameter define parameter CNT_MAX = 20'd999_999; //计数器计数最大值 //wire define wire po_key ; //------------- key_filter_inst ------------- key_filter #( .CNT_MAX (CNT_MAX ) //计数器计数最大值 ) key_filter_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .key_in (pi_key ), //按键输入信号 .key_flag (po_key ) //消抖后信号 ); //------------- flash_pp_ctrl_inst ------------- flash_pp_ctrl flash_pp_ctrl_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .key (po_key ), //按键输入信号 .sck (sck ), //片选信号 .cs_n (cs_n ), //串行时钟 .mosi (mosi ) //主输出从输入数据 ); endmodule
5. 数据连续写操作