当前位置:   article > 正文

计算机组成实验---Cache的实现

计算机组成实验---Cache的实现

直接映射

先看懂cache的映射原理,根据cache大小与主存大小来计算各个信号线的位数

各个信号线位数

主存地址在逻辑上分为区号、块号、块内地址

Cache结构

Cache访问原理

基本过程

状态机:“三段式”实现 6.3 Verilog 状态机 | 菜鸟教程 (runoob.com)

// TODO: 编写状态机现态的更新逻辑

// TODO: 编写状态机的状态转移逻辑

// TODO: 生成状态机的输出信号

状态机设计

再分析并确定各个状态下的输入输出,完善状态机。

编写状态机更新逻辑

这个类似模板了

  1. // TODO: 编写状态机现态的更新逻辑
  2. always @(posedge cpu_clk or posedge cpu_rst) begin
  3. if(cpu_rst) begin
  4. sta <= 2'b0;
  5. end
  6. else begin
  7. sta <= nex_sta;
  8. end
  9. end
编写状态机状态转移信号

需要以下变量:新的访问请求信号、命中信号、缺失(不命中)、主存数据已返回

  1. // TODO: 编写状态机的状态转移逻辑
  2. always@(*) begin
  3. if(cpu_rst) begin
  4. nex_sta = IDLE;
  5. end
  6. else begin
  7. case(sta)
  8. IDLE: begin
  9. if(inst_rreq == 1'b1) begin
  10. nex_sta = TAG_CHECK;
  11. end
  12. else begin
  13. nex_sta = IDLE;
  14. end
  15. end
  16. TAG_CHECK:begin
  17. if(hit) begin
  18. nex_sta = IDLE;
  19. end
  20. else begin
  21. nex_sta = REFILL;
  22. end
  23. end
  24. REFILL: begin
  25. if(mem_rvalid) begin
  26. nex_sta = TAG_CHECK;
  27. end
  28. else begin
  29. nex_sta = REFILL;
  30. end
  31. end
  32. default: nex_sta = IDLE;
  33. endcase
  34. end
  35. end
编写状态机输出信号

分为向主存输出和向cpu输出

IDLE状态下是赋默认值

TAG_CHECK状态下是看否命中,来确定输出(若未命中,要从主存里读东西出来,放到cache里面,再从cahce里来一次是否命中的判断)

REFILL状态下,如果主存准备信号(mem_rrdy)为真,则可以发送主存读所需要的地址和使能信号(注意只提供一个时钟周期)

错误总结:

  1. 一直在思考如何让发送给主存的信号只有一个时钟周期,由于有 mem_rrdy 的限制,以及不符合打两拍的场景,所以打两拍操作最后是不行的(打两拍可参照inst_valid和inst_out单周期有效的方法,打两拍的应用场景:要在单位信号从0到1,的属于1的那个周期里,发送一个周期的有效信号)
  2. 还有,块是整块取,块的取址地址要是模四为零的。
  3. 忘了mem_addr的有效时间只有一个周期,在后面也使用到了它,哭哭。

贴一个完整代码

  1. `timescale 1ns / 1ps
  2. `define BLK_LEN 4
  3. `define BLK_SIZE (`BLK_LEN*32)
  4. module ICache(
  5. input wire cpu_clk,
  6. input wire cpu_rst, // high active
  7. // Interface to CPU
  8. input wire inst_rreq, // 来自CPU的取指请求
  9. input wire [31:0] inst_addr, // 来自CPU的取指地址
  10. output reg inst_valid, // 输出给CPU的指令有效信号(读指令命中)
  11. output reg [31:0] inst_out, // 输出给CPU的指令
  12. // Interface to Read Bus
  13. input wire mem_rrdy, // 主存就绪信号(高电平表示主存可接收ICache的读请求)
  14. output reg [ 3:0] mem_ren, // 输出给主存的读使能信号
  15. output reg [31:0] mem_raddr, // 输出给主存的读地址
  16. input wire mem_rvalid, // 来自主存的数据有效信号
  17. input wire [`BLK_SIZE-1:0] mem_rdata // 来自主存的读数据
  18. );
  19. `ifdef ENABLE_ICACHE /******** 不要修改此行代码 ********/
  20. wire [4:0] tag_from_cpu = inst_addr[14:10]; // 主存地址的TAG
  21. wire [3:0] offset = inst_addr[3:0]; // 32位字偏移量
  22. wire valid_bit = cache_line_r[`BLK_SIZE + 5]; // Cache行的有效位
  23. wire [4:0] tag_from_cache = cache_line_r[`BLK_SIZE + 4 : `BLK_SIZE]; // Cache行的TAG
  24. // TODO: 定义ICache状态机的状态变量
  25. parameter IDLE = 2'b00;
  26. parameter TAG_CHECK = 2'b01;
  27. parameter REFILL = 2'b10;
  28. reg [1:0] sta, nex_sta;
  29. wire hit = (sta == TAG_CHECK) ? (valid_bit && (tag_from_cache == tag_from_cpu)) : 1'b0;
  30. wire[6:0] offset_bit = {offset,3'b000};
  31. wire[`BLK_SIZE + 5:0] data_out = cache_line_r >> offset_bit;
  32. always @(*) begin
  33. if(hit & hit_n) begin
  34. inst_valid = 1'b1;
  35. inst_out = data_out[31:0];
  36. /* TODO: 根据字偏移,选择Cache行中的某个32位字输出指令 */
  37. end
  38. else begin
  39. inst_valid = 1'b0;
  40. inst_out = 32'b0;
  41. end
  42. end
  43. reg hit_n ;
  44. always@(posedge cpu_clk) begin
  45. hit_n <= ~hit;
  46. end
  47. reg inst_addr_reg;
  48. wire cache_we = mem_rvalid; // ICache存储体的写使能信号
  49. wire [5:0] cache_index = inst_addr[9:4]; // 主存地址的Cache索引 / ICache存储体的地址
  50. wire [`BLK_SIZE + 5:0] cache_line_w = {1'b1,inst_addr[14:10],mem_rdata}; // 待写入ICache的Cache行
  51. wire [`BLK_SIZE + 5:0] cache_line_r; // 从ICache读出的Cache行
  52. //135 = 1(有效位) + 7(TAG的位数) + 128(数据位数)
  53. // ICache存储体:Block MEM IP核
  54. blk_mem_gen_1 U_isram (
  55. .clka (cpu_clk),
  56. .wea (cache_we),
  57. .addra (cache_index),
  58. .dina (cache_line_w),
  59. .douta (cache_line_r)
  60. );
  61. // TODO: 编写状态机现态的更新逻辑
  62. always @(posedge cpu_clk or posedge cpu_rst) begin
  63. if(cpu_rst) begin
  64. sta <= 2'b0;
  65. end
  66. else begin
  67. sta <= nex_sta;
  68. end
  69. end
  70. // TODO: 编写状态机的状态转移逻辑
  71. always@(*) begin
  72. if(cpu_rst) begin
  73. nex_sta = IDLE;
  74. end
  75. else begin
  76. case(sta)
  77. IDLE: begin
  78. if(inst_rreq == 1'b1) begin
  79. nex_sta = TAG_CHECK;
  80. end
  81. else begin
  82. nex_sta = IDLE;
  83. end
  84. end
  85. TAG_CHECK:begin
  86. if(hit) begin
  87. nex_sta = IDLE;
  88. end
  89. else begin
  90. nex_sta = REFILL;
  91. end
  92. end
  93. REFILL: begin
  94. if(mem_rvalid) begin
  95. nex_sta = TAG_CHECK;
  96. end
  97. else begin
  98. nex_sta = REFILL;
  99. end
  100. end
  101. default: nex_sta = IDLE;
  102. endcase
  103. end
  104. end
  105. // reg mem_rrdy_n;
  106. reg mem_ren_pulse;
  107. always @(posedge cpu_clk or posedge cpu_rst) begin
  108. if(cpu_rst) begin
  109. mem_ren_pulse <= 1'b0;
  110. end
  111. else if(sta == REFILL) begin
  112. if(mem_rrdy && !mem_ren_pulse) begin
  113. mem_ren_pulse <= 1'b1;
  114. end
  115. end
  116. else begin
  117. mem_ren_pulse <= 1'b0;
  118. end
  119. end
  120. // TODO: 生成状态机的输出信号
  121. always @(*) begin
  122. if(cpu_rst) begin
  123. mem_ren = 4'b0;
  124. mem_raddr = 32'b0;
  125. // mem_rrdy_n <= 1'b1;
  126. end
  127. else begin
  128. case(sta)
  129. IDLE : begin
  130. mem_ren = 4'b0;
  131. mem_raddr = 32'b0;
  132. // mem_rrdy_n <= 1'b1;
  133. end
  134. TAG_CHECK : begin
  135. mem_ren = 4'b0;
  136. mem_raddr = 32'b0;
  137. // mem_rrdy_n <= 1'b1;
  138. end
  139. REFILL: begin
  140. if(mem_rrdy && !mem_ren_pulse) begin
  141. mem_ren = 4'b1111;
  142. mem_raddr = {inst_addr[31:4],4'b0000};
  143. // mem_rrdy_n <= 1'b0;
  144. end
  145. else begin
  146. mem_ren = 4'b0;
  147. mem_raddr = 32'b0;
  148. end
  149. end
  150. default: begin
  151. mem_ren = 4'b0;
  152. mem_raddr = 32'b0;
  153. // mem_rrdy_n <= 1'b1;
  154. end
  155. endcase
  156. end
  157. end
  158. /******** 不要修改以下代码 ********/
  159. `else
  160. localparam IDLE = 2'b00;
  161. localparam STAT0 = 2'b01;
  162. localparam STAT1 = 2'b11;
  163. reg [1:0] state, nstat;
  164. always @(posedge cpu_clk or posedge cpu_rst) begin
  165. state <= cpu_rst ? IDLE : nstat;
  166. end
  167. always @(*) begin
  168. case (state)
  169. IDLE: nstat = inst_rreq ? (mem_rrdy ? STAT1 : STAT0) : IDLE;
  170. STAT0: nstat = mem_rrdy ? STAT1 : STAT0;
  171. STAT1: nstat = mem_rvalid ? IDLE : STAT1;
  172. default: nstat = IDLE;
  173. endcase
  174. end
  175. always @(posedge cpu_clk or posedge cpu_rst) begin
  176. if (cpu_rst) begin
  177. inst_valid <= 1'b0;
  178. mem_ren <= 4'h0;
  179. end else begin
  180. case (state)
  181. IDLE: begin
  182. inst_valid <= 1'b0;
  183. mem_ren <= (inst_rreq & mem_rrdy) ? 4'hF : 4'h0;
  184. mem_raddr <= inst_rreq ? inst_addr : 32'h0;
  185. end
  186. STAT0: begin
  187. mem_ren <= mem_rrdy ? 4'hF : 4'h0;
  188. end
  189. STAT1: begin
  190. mem_ren <= 4'h0;
  191. inst_valid <= mem_rvalid ? 1'b1 : 1'b0;
  192. inst_out <= mem_rvalid ? mem_rdata[31:0] : 32'h0;
  193. end
  194. default: begin
  195. inst_valid <= 1'b0;
  196. mem_ren <= 4'h0;
  197. end
  198. endcase
  199. end
  200. end
  201. `endif
  202. endmodule

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/687104
推荐阅读
相关标签
  

闽ICP备14008679号