• 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十七:IIC储存模块


    1. int main()
    
    2. {
    
    3. int A;
    
    4. A = 165. }

    代码17.1

    话题为进入之前,首先让我们来聊聊一些题外话。那些学过软核NIOS的朋友可曾记得,软核NIOS可利用片上内存作为储存资源,而且它也能利用SDRAM作为储存资源,然而问题是在这里 ... 如代码17.1所示,笔者先建立变量A,然后变量A赋值16。如果站在高级语言上的角度去思考,无论是建立变量A还是为变量A赋值,我们没有必要去理解变量A利用什么储存资源,然后赋值变量A又是利用怎样的储存功能去实现。

    我们只要负责轻松的表层工作,然而那些辛苦的底层工作统统交由编译器处理。读者也许会认为那是高级语言的温柔,不过这股温柔却不适合描述语言,还不如说那是一种抹杀性的伤害。这种感觉好比父母亲过度溺爱自己的孩子,娇生惯养的孩子最终只会失去可能性而已。

    笔者曾在前面说过,储存模块基本上可以分为“储存资源”还有“储存方式”。默认下,描述语言可用的储存资源有寄存器还有片上内存,然而两者都是内在资源。换之,实验十六却利用外在的储存资源,IIC储存器。

    clip_image002

    图17.1 IIC储存模块与RAM储存模块。

    如图17.1所示,那是实验十六的IIC储存模块,然而图17.1也表示IIC储存模块的调用方法也不仅近似 RAM储存模块,而且只有一方调用它而已。这种感觉好比顺序语言的主函数调用某个储存函数一样,结果如代码17.2所示:

    1. int ram_func( int Addr, int WrData ) { ... }
    
    2.
    
    3. int main()
    
    4. {
    
    5. ram_func( 020 );
    
    6. ...
    
    7. }

    代码17.2

    如代码17.2所示,第1行声明函数 ram_func,然后主函数在第5行将其调用,并且传递地址参数0,数据参数 20。高级语言是一位顺序又寂寞的家伙,函数永远只能被一方调用而已 ... 换之,描述语言是一位并行又多愁的家伙,模块有可能同时被两方以上调用,情况宛如两位男生同时追求一位少女,对此少女会烦恼选择谁。哎~这是奢侈的少女忧愁。

    clip_image004

    图17.2 双口RAM储存模块。

    如图17.2所示,RAM储存模块同时被两方调用,周边操作为它写入数据,核心操作则为它读出数据。图17.2也是俗称的双口RAM。对此,我们也可以说双口RAM储存模是RAM储存模块的亚种。

    clip_image006

    图17.3 基于双口RAM储存模块的FIFO储存模块。

    此外,双口RAM储存模块只要稍微更换一下马甲,然后加入一些先进先出的机制,随之基于双口RAM储存模块的FIFO储存模块便完成,结果如图17.3所示。对此,我们可以说FIFO储存模块是双口RAM储存模块的亚种。

    那么问题来了:

    “请问,实验十六的IIC储存模块是否也能成为双口IIC储存模块?”,笔者问道。

    “再请问,它还可以成为有FIFO机制的储存模块呢?”,笔者再问道。

    没错,上述问题就是实验十七的主要目的。如果这些要求可以成真,我们便可以断定描述语言不仅不逊色与顺序语言,描述语言也充满许多可能性,而且储存类作为一个模块类有着举足轻重的地位。废话少说,我们还是开始实验吧,因为笔者已经压抑不了蛋蛋的冲动!

    首先我们要明白,片上内存是效率又优秀的储存资源,基本上只要1个时钟就可完成读写操作,而且读写也可以同时进行,两方也可以同时调用,不过就是不能随意扩充。反之,IIC储存器虽然可以随意扩充,但是又笨又麻烦的它,读写操作不仅用时很长,而且不能也同时进行,对此造就两方不能同时调用的问题。为此,我们必须先解决这个问题。

    clip_image008

    图17.4 写操作与读操作。

    实验十六告诉我们,IIC储存模块有两位 Call 信号,其中 Call[1] 表示写操作,Call[0]表示读操作。不过不管是写操作还是读操作,IIC储存模块都必须调用IIC储存器,而且读写操作一次也只能进行其中一项。如图17.4,假设左边的周边操作负责写操作,右边的核心操作负责读操作 ... 如果两者同时拉高 Call 信号就会发生多义性的问题,对此笔者该如何协调呢?

    clip_image010

    图17.5 轮流协调。

    为了公平起见,笔者采取轮流的方式来协调多义性的问题。图17.5所是轮流协调的概念图,一般Call兼职“提问与使能“,即Call一拉高操作便执行。如今Call信号作为第一层提问,isDo则作为第二层使能 ... 换句话说,不管 Call 拉不拉高,只要isDo不拉高,操作也不会执行。如图17.5所示,isDo位宽有3表示模块有3种操作,或者说3个操作共享一个模块资源。至于右边是称为使能指针的箭头,它的作用是给予使能权。

    clip_image012

    图17.6 轮流协调例子①。

    如图17.6所示,假设 Call[2] 拉高以示提问,但是指针并没有指向它,所以它没有使能权也不能执行操作。这种情况好比举手的学生没被老师点名,这位学生就不能随意开口。当然,使能指针也不是静止不动,只要遇见有人举手提问,它便会按照顺序检测各个对象。

    clip_image014

    图17.7 轮流协调例子②。

    如图17.7所示,当指针来到Call[2]的面前并且给予使能权,isDo[2]立即拉高使能操作,直至操作完成之前,该操作都享有模块的使用权。(灰度指针为过去,黑色指针为现在)

    clip_image016

    图17.8 轮流协调例子③。

    如图17.8所示,操作执行完毕之际,模块便会反馈完成信号以示结束操作,isDo[2] 还有Call[2] 都会经由完成信号拉低内容。此外,指针也会立即指向下一个对象。

    clip_image018

    图17.9 轮流协调例子④。

    如图17.9所示,假设 Call[2] 还有 Call[1] 同时提问,由于指针没有指向它们,所以Call[2] 与 Call[1] 都没有使能权。此刻,指针开始一动。

    clip_image020

    图17.10 轮流协调例子⑤。

    首先,Call[1] 会得到使能权,isDo[1]因此拉高并且开始执行操作,直至操作结束之前,isDo[1]都独占模块,结果如图17.10所示。

    clip_image022

    图17.11 轮流协调例子⑥。

    如图17.11所示,当操作执行完毕,模块便会反馈完成信号,随之isDo[1] 还有 Call[1]都会拉低内容,而且指针也会指向下一个对象。对此,isDo[2] 得到使能权,并且开始执行操作 ... 直至操作结束之前,它都独占模块。

    clip_image024

    图17.12 轮流协调例子⑦。

    操作结束之际,模块便会反馈完成信号,isDo[2] 还有 Call[2] 随之也会拉低内容,然后指针指向另一个对象,结果如图17.12所示。轮流协调的概念基本上就是这样而已,即单纯也非常逻辑。接下来,让我们来看看Verilog 如何描述轮流协调,结果如代码17.3所示:

    1. module iic_savemod
    
    2. (
    
    3. input [1:0]iCall,
    
    4. output [1:0]oDone,
    
    5. );
    
    6. reg [1:0]C7;
    
    7. reg [1:0]isDo;
    
    8.
    
    9. always @ ( posedge CLOCK or negedge RESET )
    
    10. if( !RESET ) 
    
    11. begin 
    
    12. C7 <= 2’b10;
    
    13. isDo <= 2’b00;
    
    14. end
    
    15. else 
    
    16. begin
    
    17. if( iCall[1] & C7[1] ) isDo[1] <= 1’b1;
    
    18. else if( iCall[0] & C7[0] ) isDo[0] <= 1’b1;
    
    19.
    
    20. if( isDo[1] & isDone[1] ) isDo[1] <= 1’b0;
    
    21. else if( isDo[0] & isDone[0] ) isDo[0] <= 1’b0;
    
    22.
    
    23. if( isDone ) C7 <= { isDo[0], isDo[1] };
    
    24. else if( iCall ) C7 <= { C7[0], C7[1] };
    
    25. end
    
    26.

    代码17.3

    第3~4行是相关的出入端声明, 其中 Call 还有 Done 均为两位。第6~7行是轮流协调作用的寄存器isDo与C7,C7为使能指针。第10~14行则是这些寄存器的初始化,注意C7默认下指向 Call[1]。第16~25行则是轮流协调的主要操作,第17~18行是提问与使能,其中 iCall[N] & C7[N] 表示提问并且被指针指向,isDo[N] 表示给予使能权。

    第20~21行是消除提问和使能,其中 isDo[N] & isDone[N] 表示相关的完成信号对应相关的操作,然后 isDo[N] 表示消除使能。第24行的表示有提问,指针就立即移动。第23行表示结束操作,指针便指向下一个对象。

    27. reg [4:0]i;
    
    28. reg [1:0]isDone;
    
    29.
    
    30. always @ ( posedge CLOCK or negedge RESET )
    
    31. if( !RESET )
    
    32. begin
    
    33. ...
    
    34. i <= 5’d0;
    
    35. isDone <= 2’b00;
    
    36. end
    
    37. else if( isDo[1] )
    
    38. case( i )
    
    39. ...
    
    40. 5: begin isDone[1] <= 1’b1; i <= i + 1’b1; end
    
    41. 6: begin isDone[1] <= 1’b0; i <= 5’d0; end
    
    42. endcase
    
    43. else if( isDo[0] )
    
    44. case( i )
    
    45. ...
    
    46. 7: begin isDone[0] <= 1’b1; i <= i + 1’b1; end
    
    47. 8: begin isDone[0] <= 1’b0; i <= 5’d0; end
    
    48. endcase
    
    49.
    
    50. endmodule

    代码17.3

    第27~28行只核心操作相关的寄存器,第31~36行则是这些寄存器的复位操作。第37行表示 isDo[1] 拉高才执行操作1。第40~41行表示操作1反馈完成信号。第43行表示 isDo[0] 拉高才指向操作0。第46~47行表示操作0反馈完成信号。如此一来,问答信号便有轮流协调,接下来就是为IIC储存模块加入FIFO机制。

    clip_image026

    图17.13 有FIFO机制的IIC储存模块。

    如图17.13所示,那是拥有FIFO机制的IIC储存模块,它那畸形的储存功能,可谓是IIC储存模块的亚种。其中 Call/Done[1] 表示写入调用,Tag[1] 表示写满状态,反之既然。由于目前的IIC储存模块是FIFO的关系,所以写入地址还有读出地址都是在里边建立。为此,Verilog可以这样描述,结果如代码17.4所示:

    1. module iic_savemod
    
    2. (
    
    3. input [1:0]iCall,
    
    4. output [1:0]oDone,
    
    5. input [7:0]iData,
    
    6. output [7:0]oData,
    
    7. output [1:0]oTag
    
    8. );
    
    9. always @ ( posedge CLOCK or negedge RESET ) // 轮流协调的周边操作
    
    10. ...
    
    11.
    
    12. reg [8:0]C2,C3; // C2 Write Pointer, C3 Read Pointer;
    
    13.
    
    14. always @ ( posedge CLOCK or negedge RESET )
    
    15. if( !RESET )
    
    16. begin
    
    17. C2 <= 9’d0;
    
    18. C3 <= 9’d0;
    
    19. end
    
    20. else if( isDo[1] )
    
    21. case( i )
    
    22. ...
    
    23. 2: // Wirte Word Addr
    
    24. begin D1 <= C2[7:0]; i <= FF_Write1; Go <= i + 1'b1; end
    
    25. ...
    
    26. 5:
    
    27. begin C2 <= C2 + 1'b1; isDone[1] <= 1'b1; i <= i + 1'b1; end
    
    28. ...
    
    29. endcase
    
    30. else if( isDo[0] )
    
    31. case( i )
    
    32. ...
    
    33. 2: // Wirte Word Addr
    
    34. begin D1 <= C3[7:0]; i <= FF_Write2; Go <= i + 1'b1; end
    
    35. ...
    
    36. 7:
    
    37. begin C3 <= C3 + 1'b1; isDone[0] <= 1'b1; i <= i + 1'b1; end
    
    38. ...
    
    39. endcase
    
    40.
    
    41. ...
    
    42. assign oTag[1] = ( (C2[8]^C3[8]) && (C2[7:0] == C3[7:0]) );
    
    43. assign oTag[0] = ( C2 == C3 );
    
    44.
    
    45. endmodule

    代码17.4

    如代码17.4所示,第3~7行是相关的出入端声明。第12行建立相关的寄存器,C2为写指针,C3为读指针,位宽为 N + 1。第23~24行表示C2[7:0]为写数据地址。第26~27行表示C2递增。第33~34行表示C3[7:0]为读数据地址。第36~37行表示C3递增。第42行表示写满状态,第43行则表示读空状态。完后,我们便可以开始建模了。

    clip_image028

    图17.14 实验十七的建模图。

    图17.14是实验十七的建模图,周边操作为 IIC 储存模块写入数据,核心操作则从哪里读取数据,并且将读出的数据驱动数码管基础模块。

    iic_savemod.v

    clip_image030

    图17.15 IIC储存模块。

    图17.15是IIC储存模块的建模图,左方是写入操作,右边是读出操作,上方则是链接至顶层信号 SCL 与 SDA。

    1. module iic_savemod
    
    2. (
    
    3. input CLOCK, RESET,
    
    4. output SCL,
    
    5. inout SDA,
    
    6. input [1:0]iCall,
    
    7. output [1:0]oDone,
    
    8. input [7:0]iData,
    
    9. output [7:0]oData,
    
    10. output [1:0]oTag
    
    11. );
    
    12. parameter FCLK = 10'd125, FHALF = 10'd62, FQUARTER = 10'd31; //(1/400E+3)/(1/50E+6)
    
    13. parameter THIGH = 10'd30, TLOW = 10'd65, TR = 10'd15, TF = 10'd15;
    
    14. parameter THD_STA = 10'd30, TSU_STA = 10'd30, TSU_STO = 10'd30;
    
    15. parameter FF_Write1 = 5'd7;
    
    16. parameter FF_Write2 = 5'd9, FF_Read = 5'd19;
    
    17.
    
    18. /***************/
    
    19.
    
    20. reg [1:0]C7;
    
    21. reg [1:0]isDo;
    
    22.
    
    23. always @ ( posedge CLOCK or negedge RESET )
    
    24. if( !RESET )
    
    25. begin
    
    26. C7 <= 2'b10;
    
    27. isDo <= 2'b00;
    
    28. end
    
    29. else
    
    30. begin
    
    31.
    
    32. if( iCall[1] & C7[1] ) isDo[1] <= 1'b1;
    
    33. else if( iCall[0] & C7[0] ) isDo[0] <= 1'b1;
    
    34.
    
    35. if( isDo[1] & isDone[1] ) isDo[1] <= 1'b0;
    
    36. else if( isDo[0] & isDone[0] ) isDo[0] <= 1'b0;
    
    37.
    
    38. if( isDone ) C7 <= {isDo[0],isDo[1]};
    
    39. else if( iCall ) C7 <= { C7[0], C7[1] };
    
    40.
    
    41. end
    
    42.
    
    43.
    
    44. /***************/
    
    45.
    
    46. reg [4:0]i;
    
    47. reg [4:0]Go;
    
    48. reg [9:0]C1;
    
    49. reg [7:0]D1;
    
    50. reg [1:0]isDone;
    
    51. reg [8:0]C2,C3; // C2 Write Pointer, C3 Read Pointer
    
    52. reg rSCL,rSDA;
    
    53. reg isAck,isQ;
    
    54.
    
    55. always @ ( posedge CLOCK or negedge RESET )
    
    56. if( !RESET )
    
    57. begin
    
    58. { i,Go } <= { 5'd0,5'd0 };
    
    59. C1 <= 10'd0;
    
    60. D1 <= 8'd0;
    
    61. isDone <= 2'd0;
    
    62. { C2, C3 } <= 18'd0;
    
    63. { rSCL,rSDA,isAck,isQ } <= 4'b1111;
    
    64. end
    
    65. else if( isDo[1] )
    
    66. case( i )
    
    67.
    
    68. 0: // Call
    
    69. begin
    
    70. isQ = 1;
    
    71. rSCL <= 1'b1;
    
    72.
    
    73. if( C1 == 0 ) rSDA <= 1'b1; 
    
    74. else if( C1 == (TR + THIGH) ) rSDA <= 1'b0;
    
    75.
    
    76. if( C1 == (FCLK) -1) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    77. else C1 <= C1 + 1'b1;
    
    78. end
    
    79.
    
    80. 1: // Write Device Addr
    
    81. begin D1 <= {4'b1010, 3'b000, 1'b0}; i <= 5'd7; Go <= i + 1'b1; end
    
    82.
    
    83. 2: // Wirte Word Addr
    
    84. begin D1 <= C2[7:0]; i <= FF_Write1; Go <= i + 1'b1; end
    
    85.
    
    86. 3: // Write Data
    
    87. begin D1 <= iData; i <= FF_Write1; Go <= i + 1'b1; end
    
    88.
    
    89. /*************************/
    
    90.
    
    91. 4: // Stop
    
    92. begin
    
    93. isQ = 1'b1;
    
    94.
    
    95. if( C1 == 0 ) rSCL <= 1'b0;
    
    96. else if( C1 == FQUARTER ) rSCL <= 1'b1; 
    
    97.
    
    98. if( C1 == 0 ) rSDA <= 1'b0;
    
    99. else if( C1 == (FQUARTER + TR + TSU_STO ) ) rSDA <= 1'b1;
    
    100.
    
    101. if( C1 == (FQUARTER + FCLK) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    102. else C1 <= C1 + 1'b1; 
    
    103. end
    
    104.
    
    105. 5:
    
    106. begin C2 <= C2 + 1'b1; isDone[1] <= 1'b1; i <= i + 1'b1; end
    
    107.
    
    108. 6: 
    
    109. begin isDone[1] <= 1'b0; i <= 5'd0; end
    
    110.
    
    111. /*******************************/ //function
    
    112.
    
    113. 7,8,9,10,11,12,13,14:
    
    114. begin
    
    115. isQ = 1'b1;
    
    116. rSDA <= D1[14-i];
    
    117.
    
    118. if( C1 == 0 ) rSCL <= 1'b0;
    
    119. else if( C1 == (TF + TLOW) ) rSCL <= 1'b1; 
    
    120.
    
    121. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    122. else C1 <= C1 + 1'b1;
    
    123. end
    
    124.
    
    125. 15: // waiting for acknowledge
    
    126. begin
    
    127. isQ = 1'b0;
    
    128. if( C1 == FHALF ) isAck <= SDA;
    
    129.
    
    130. if( C1 == 0 ) rSCL <= 1'b0;
    
    131. else if( C1 == FHALF ) rSCL <= 1'b1;
    
    132.
    
    133. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    134. else C1 <= C1 + 1'b1; 
    
    135. end
    
    136.
    
    137. 16:
    
    138. if( isAck != 0 ) i <= 5'd0;
    
    139. else i <= Go; 
    
    140.
    
    141. /*******************************/ // end function
    
    142.
    
    143. endcase
    
    144.
    
    145. else if( isDo[0] ) 
    
    146. case( i )
    
    147.
    
    148. 0: // Call
    
    149. begin
    
    150. isQ = 1; 
    
    151. rSCL <= 1'b1;
    
    152.
    
    153. if( C1 == 0 ) rSDA <= 1'b1; 
    
    154. else if( C1 == (TR + THIGH) ) rSDA <= 1'b0;
    
    155.
    
    156. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    157. else C1 <= C1 + 1'b1;
    
    158. end
    
    159.
    
    160. 1: // Write Device Addr
    
    161. begin D1 <= {4'b1010, 3'b000, 1'b0}; i <= 5'd9; Go <= i + 1'b1; end
    
    162.
    
    163. 2: // Wirte Word Addr
    
    164. begin D1 <= C3[7:0]; i <= FF_Write2; Go <= i + 1'b1; end
    
    165.
    
    166. 3: // Start again
    
    167. begin
    
    168. isQ = 1'b1;
    
    169.
    
    170. if( C1 == 0 ) rSCL <= 1'b0;
    
    171. else if( C1 == FQUARTER ) rSCL <= 1'b1;
    
    172. else if( C1 == (FQUARTER + TR + TSU_STA + THD_STA + TF) ) rSCL <= 1'b0;
    
    173.
    
    174. if( C1 == 0 ) rSDA <= 1'b0; 
    
    175. else if( C1 == FQUARTER ) rSDA <= 1'b1;
    
    176. else if( C1 == ( FQUARTER + TR + THIGH) ) rSDA <= 1'b0;
    
    177.
    
    178. if( C1 == (FQUARTER + FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    179. else C1 <= C1 + 1'b1;
    
    180. end
    
    181.
    
    182. 4: // Write Device Addr ( Read )
    
    183. begin D1 <= {4'b1010, 3'b000, 1'b1}; i <= 5'd9; Go <= i + 1'b1; end
    
    184.
    
    185. 5: // Read Data
    
    186. begin D1 <= 8'd0; i <= FF_Read; Go <= i + 1'b1; end
    
    187.
    
    188. 6: // Stop
    
    189. begin
    
    190. isQ = 1'b1;
    
    191.
    
    192. if( C1 == 0 ) rSCL <= 1'b0;
    
    193. else if( C1 == FQUARTER ) rSCL <= 1'b1; 
    
    194.
    
    195. if( C1 == 0 ) rSDA <= 1'b0;
    
    196. else if( C1 == (FQUARTER + TR + TSU_STO) ) rSDA <= 1'b1;
    
    197.
    
    198. if( C1 == (FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    199. else C1 <= C1 + 1'b1; 
    
    200. end
    
    201.
    
    202. 7:
    
    203. begin C3 <= C3 + 1'b1; isDone[0] <= 1'b1; i <= i + 1'b1; end
    
    204.
    
    205. 8: 
    
    206. begin isDone[0] <= 1'b0; i <= 5'd0; end
    
    207.
    
    208. /*******************************/ //function
    
    209.
    
    210. 9,10,11,12,13,14,15,16:
    
    211. begin
    
    212. isQ = 1'b1;
    
    213.
    
    214. rSDA <= D1[16-i];
    
    215.
    
    216. if( C1 == 0 ) rSCL <= 1'b0;
    
    217. else if( C1 == (TF + TLOW) ) rSCL <= 1'b1; 
    
    218.
    
    219. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    220. else C1 <= C1 + 1'b1;
    
    221. end
    
    222.
    
    223. 17: // waiting for acknowledge
    
    224. begin
    
    225. isQ = 1'b0;
    
    226.
    
    227. if( C1 == FHALF ) isAck <= SDA;
    
    228.
    
    229. if( C1 == 0 ) rSCL <= 1'b0;
    
    230. else if( C1 == FHALF ) rSCL <= 1'b1;
    
    231.
    
    232. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    233. else C1 <= C1 + 1'b1; 
    
    234. end
    
    235.
    
    236. 18:
    
    237. if( isAck != 0 ) i <= 5'd0;
    
    238. else i <= Go;
    
    239.
    
    240. /*****************************/
    
    241.
    
    242. 19,20,21,22,23,24,25,26: // Read
    
    243. begin
    
    244. isQ = 1'b0;
    
    245. if( C1 == FHALF ) D1[26-i] <= SDA;
    
    246.
    
    247. if( C1 == 0 ) rSCL <= 1'b0;
    
    248. else if( C1 == FHALF ) rSCL <= 1'b1; 
    
    249.
    
    250. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
    
    251. else C1 <= C1 + 1'b1;
    
    252. end
    
    253.
    
    254. 27: // no acknowledge
    
    255. begin
    
    256. isQ = 1'b1;
    
    257. //if( C1 == 100 ) isAck <= SDA;
    
    258.
    
    259. if( C1 == 0 ) rSCL <= 1'b0;
    
    260. else if( C1 == FHALF ) rSCL <= 1'b1;
    
    261.
    
    262. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= Go; end
    
    263. else C1 <= C1 + 1'b1; 
    
    264. end
    
    265.
    
    266. /*************************************/ // end fucntion
    
    267.
    
    268. endcase
    
    269.
    
    270. /***************************************/
    
    271.
    
    272. assign SCL = rSCL;
    
    273. assign SDA = isQ ? rSDA : 1'bz;
    
    274. assign oDone = isDone;
    
    275. assign oData = D1;
    
    276. assign oTag[1] = ( (C2[8]^C3[8]) && (C2[7:0] == C3[7:0]) );
    
    277. assign oTag[0] = ( C2 == C3 );
    
    278.
    
    279. /***************************************/
    
    280.
    
    281. endmodule

    具体内容笔者也懒得解释了,读者自己看着办吧。

    iic_demo.v

    连线部署请参考图17.14。

    1. module iic_demo
    
    2. (
    
    3. input CLOCK, RESET,
    
    4. output SCL,
    
    5. inout SDA,
    
    6. output [7:0]DIG,
    
    7. output [5:0]SEL
    
    8. );
    
    以上内容为相关的出入端声明。
    
    9. reg [3:0]j;
    
    10. reg [7:0]D1;
    
    11. reg isWR;
    
    12.
    
    13. always @ ( posedge CLOCK or negedge RESET )
    
    14. if( !RESET )
    
    15. begin 
    
    16. j <= 4'd0;
    
    17. D1 <= 8'd0;
    
    18. isWR <= 1'b0;
    
    19. end
    
    20. else
    
    21. case( j )
    
    22.
    
    23. 0:
    
    24. if( !TagU1[1] ) j <= j + 1'b1;
    
    25.
    
    26. 1:
    
    27. if( DoneU1[1] ) begin isWR <= 1'b0; j <= j + 1'b1; end
    
    28. else begin isWR <= 1'b1; D1 <= 8'hAB; end
    
    29.
    
    30. 2:
    
    31. if( !TagU1[1] ) j <= j + 1'b1;
    
    32.
    
    33. 3:
    
    34. if( DoneU1[1] ) begin isWR <= 1'b0; j <= j + 1'b1; end
    
    35. else begin isWR <= 1'b1; D1 <= 8'hCD; end
    
    36.
    
    37. 4:
    
    38. if( !TagU1[1] ) j <= j + 1'b1;
    
    39.
    
    40. 5:
    
    41. if( DoneU1[1] ) begin isWR <= 1'b0; j <= j + 1'b1; end
    
    42. else begin isWR <= 1'b1; D1 <= 8'hEF; end
    
    43.
    
    44. 6:
    
    45. i <= i;
    
    46.
    
    47. endcase
    
    48.

    以上内容为写入作用的周边操作,操作过程如下:

    步骤0,判断是否写满状态。

    步骤1,写入数据 8’hAB;

    步骤2,判断是否写满状态。

    步骤3,写入数据 8’hCD;

    步骤4,判断是否写满状态。

    步骤5,写入数据 8’hEF;

    步骤6,发呆。

    49. wire [7:0]DataU1;
    
    50. wire [1:0]DoneU1;
    
    51. wire [1:0]TagU1;
    
    52.
    
    53. iic_savemod U1
    
    54. (
    
    55. .CLOCK( CLOCK ),
    
    56. .RESET( RESET ),
    
    57. .SCL( SCL ), // > top
    
    58. .SDA( SDA ), // <> top
    
    59. .iCall( { isWR, isRD } ), // < sub & core
    
    60. .oDone( DoneU1 ), // > core
    
    61. .iData( D1 ), // < core
    
    62. .oData( DataU1 ), // > core
    
    63. .oTag( TagU1 )
    
    64. );
    
    65.

    以上内容为IIC储存模块的实例化。第59行表示 isWR 为 Call[1],isRD 为 Call[0]。第61行表示 D1 驱动该输入。

    66. reg [3:0]i;
    
    67. reg [23:0]D2;
    
    68. reg isRD;
    
    69.
    
    70. always @ ( posedge CLOCK or negedge RESET ) // core
    
    71. if( !RESET )
    
    72. begin
    
    73. i <= 4'd0;
    
    74. D2 <= 24'd0;
    
    75. isRD <= 1'b0;
    
    76. end
    
    77. else
    
    78. case( i )
    
    79.
    
    80. 0:
    
    81. if( !TagU1[0] ) i <= i + 1'b1;
    
    82.
    
    83. 1:
    
    84. if( DoneU1[0] ) begin D2[23:16] <= DataU1; isRD <= 1'b0; i <= i + 1'b1; end
    
    85. else isRD <= 1'b1;
    
    86.
    
    87. 2:
    
    88. if( !TagU1[0] ) i <= i + 1'b1;
    
    89.
    
    90. 3:
    
    91. if( DoneU1[0] ) begin D2[15:8] <= DataU1; isRD <= 1'b0; i <= i + 1'b1; end
    
    92. else isRD <= 1'b1;
    
    93.
    
    94. 4:
    
    95. if( !TagU1[0] ) i <= i + 1'b1;
    
    96.
    
    97. 5:
    
    98. if( DoneU1[0] ) begin D2[7:0] <= DataU1; isRD <= 1'b0; i <= i + 1'b1; end
    
    99. else isRD <= 1'b1;
    
    100.
    
    101. 6:
    
    102. i <= i;
    
    103.
    
    104. endcase
    
    105.

    以上内容为读出数据并且驱动数码管基础模块的核心操作,操作过程如下:

    步骤0,判断是否为读空状态。

    步骤1,读出数据 8’hAB,并且暂存至 D2[23:16]。

    步骤2,判断是否为读空状态。

    步骤3,读出数据 8’hCD,并且暂存至 D2[15:8]。

    步骤4,判断是否为读空状态。

    步骤5,读出数据 8’hEF,并且暂存至 D2[7:0]。

    步骤6,发呆。

    106. smg_basemod U2
    
    107. (
    
    108. .CLOCK( CLOCK ),
    
    109. .RESET( RESET ),
    
    110. .DIG( DIG ), // > top
    
    111. .SEL( SEL ), // > top
    
    112. .iData( D2 ) // < core
    
    113. );
    
    114.
    
    115. endmodule

    第106~113行是数码管基础模块的实例化,第112行表示D2驱动该输入。编译完毕并且下载程序,如果数码管自左向右显示“ABCDEF”表示实验成功。

    细节一: 完整的个体模块

    实验十七的IIC储存模块随时可以使用。

  • 相关阅读:
    我这里面所用的DBHelper
    同时向主表和从表里面导入execl数据 (asp.net webform)
    在asp.net webform中的 gridview 里面的一些基本操作
    在ASP.NET WEBFORM 中后台实现gridview全选功能
    asp.net webform 发送电子邮件
    Asp.Net中的三种分页方式
    asp.net获取客户端浏览器及主机信息
    在asp.net webfrom 中上传execl (读取单个sheet的数据)
    Linux五种IO模型性能分析
    epoll/poll/select的原理
  • 原文地址:https://www.cnblogs.com/alinx/p/4282993.html
Copyright © 2020-2023  润新知