• 【黑金原创教程】【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储存模块随时可以使用。

  • 相关阅读:
    【BZOJ 4581】【Usaco2016 Open】Field Reduction
    【BZOJ 4582】【Usaco2016 Open】Diamond Collector
    【BZOJ 4580】【Usaco2016 Open】248
    【BZOJ 3754】Tree之最小方差树
    【51Nod 1501】【算法马拉松 19D】石头剪刀布威力加强版
    【51Nod 1622】【算法马拉松 19C】集合对
    【51Nod 1616】【算法马拉松 19B】最小集合
    【51Nod 1674】【算法马拉松 19A】区间的价值 V2
    【BZOJ 2541】【Vijos 1366】【CTSC 2000】冰原探险
    【BZOJ 1065】【Vijos 1826】【NOI 2008】奥运物流
  • 原文地址:https://www.cnblogs.com/alinx/p/4282993.html
Copyright © 2020-2023  润新知