【时钟模块设计】-RTC实时时钟芯片DS1302 读操作
分析
首先看看读操作的时序图
大体上和写操作是一样的,不同点就在于,读操作的第二个字节是从DS1302芯片中读数传给FPGA,
于是IO端口在第一个字节是输出端,在第二个字节就是输入端了,这里就存在一个既能作为输入也能作为输出的端口
因为不知道如何实现这个功能,于是去网上搜,最后知道人家有一个专有名词,叫:双向端口
定义inout实现功能,于是我又去查这个inout是怎么用的
看了不少文章,有的写得太多了,没看明白,随后找到特权同学这篇:
我理解的inout的用法如下:
1.在模块开始时定义一个inout端口
inout IO
2.用条件判断语句给端口赋值
当端口被赋值为高阻态就表明此时端口为输入端
assign IO = io_control ? data[0] : 1'bz ;
其中,io_control =1时,IO为输出端;io_control =0时,IO为输入端,
data就是需要在IO上传输的数据
3.根据data在不同状态的值,就可以得到最后的IO信号
always @(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) data <= 8'd0; else if(state == IDLE && next_state == DRR) data <= ADDRESS_8; else if(cnt_4 == 2'd3 && state == DRR) data <= {1'b0,data[7:1]}; else if(cnt_4 == 2'd3 && state == READ) data <= {IO,data[7:1]}; end
仿真
一、设计文件
module r_ctrl ( input clk , input rst_n , input key , output CE , output reg SCK , output [7:0]SAVE , output reg done , inout IO ); parameter IDLE = 3'b001 , DRR = 3'b010 , READ = 3'b100; parameter ADDRESS_8 = 8'b0010_1001; reg [2:0] state ; reg [2:0] next_state ; // 计满自动清0 reg [1:0] cnt_4 ; reg [2:0] cnt_8 ; reg [7:0] data; // 双向IO控制端 reg io_control ; // 状态机 always @(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) state <= IDLE; else state <= next_state; end always @(*) case(state) IDLE: if(key == 1'b1) next_state <= DRR; DRR: if((cnt_8 == 3'd7)&& (cnt_4 == 2'd3) ) next_state <= READ; READ: if((cnt_8 == 3'd7)&& (cnt_4 == 2'd3) ) next_state <= IDLE; default:next_state <= IDLE; endcase always @(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) data <= 8'd0; else if(state == IDLE && next_state == DRR) data <= ADDRESS_8; else if(cnt_4 == 2'd3 && state == DRR) data <= {1'b0,data[7:1]}; else if(cnt_4 == 2'd3 && state == READ) data <= {IO,data[7:1]}; end always @(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) done <= 1'd0; else if(state == READ && next_state == IDLE) done <= 1'd1; else done <= 1'd0; end assign SAVE = data; assign IO = io_control ? data[0] : 1'bz ; always @(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) io_control <= 1'b0; else if(state == DRR) io_control <= 1'b1;// inout为输出端口 else io_control <= 1'b0; end always @(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) SCK <= 1'b0; else if(cnt_4[0]) SCK <= ~SCK; end assign CE = (state != IDLE); // 这个计数器用来计数SCK的时钟周期的大小 always @(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) cnt_4 <= 2'b0; else if(state != IDLE) cnt_4 <= cnt_4+2'b1; end always @(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) cnt_8 <= 3'b0; else if(cnt_4 == 2'd3) begin// 此处cnt_4=3时,cnt_16+1;和IO数据下降沿更新同时改变,这样就能计数传了多少个数了 cnt_8 <= cnt_8+3'd1; if((cnt_8 == 3'd7)) cnt_8 <= 3'b0; end end endmodule
二、测试文件
wait、force
这里的仿真文件学到了新东西:
`timescale 1ns/1ns module tb_r_ctrl; reg clk; reg rst_n ; reg key; wire CE; wire SCK; wire IO; wire [7:0] SAVE; wire done; initial begin clk = 1'b0; rst_n = 1'b0; key = 1'b0; #10 rst_n =1'b1; #10 key = 1'b1; #20 key = 1'b0; wait(r_ctrl_inst.state == r_ctrl_inst.READ) force IO = 1'b0; @ (negedge SCK) force IO = 1'b0; @ (negedge SCK) force IO = 1'b1; @ (negedge SCK) force IO = 1'b0; @ (negedge SCK) force IO = 1'b1; end always #10 clk = ~ clk; r_ctrl r_ctrl_inst ( .clk(clk) , .rst_n(rst_n) , .key(key) , .CE(CE) , .SCK(SCK) , .SAVE(SAVE) , .done(done) , .IO(IO) ); endmodule
三、波形图