| 1 | `include "ARM_Constants.v" |
| 2 | |
| 3 | module Memory( |
| 4 | input clk, |
| 5 | input Nrst, |
| 6 | |
| 7 | /* bus interface */ |
| 8 | output reg [31:0] busaddr, |
| 9 | output reg rd_req, |
| 10 | output reg wr_req, |
| 11 | input rw_wait, |
| 12 | output reg [31:0] wr_data, |
| 13 | input [31:0] rd_data, |
| 14 | |
| 15 | /* regfile interface */ |
| 16 | output reg [3:0] st_read, |
| 17 | input [31:0] st_data, |
| 18 | |
| 19 | /* Coprocessor interface */ |
| 20 | output reg cp_req, |
| 21 | input cp_ack, |
| 22 | input cp_busy, |
| 23 | output cp_rnw, /* 1 = read from CP, 0 = write to CP */ |
| 24 | input [31:0] cp_read, |
| 25 | output reg [31:0] cp_write, |
| 26 | |
| 27 | /* stage inputs */ |
| 28 | input inbubble, |
| 29 | input [31:0] pc, |
| 30 | input [31:0] insn, |
| 31 | input [31:0] op0, |
| 32 | input [31:0] op1, |
| 33 | input [31:0] op2, |
| 34 | input [31:0] spsr, |
| 35 | input [31:0] cpsr, |
| 36 | input write_reg, |
| 37 | input [3:0] write_num, |
| 38 | input [31:0] write_data, |
| 39 | |
| 40 | /* outputs */ |
| 41 | output reg outstall, |
| 42 | output reg outbubble, |
| 43 | output reg [31:0] outpc, |
| 44 | output reg [31:0] outinsn, |
| 45 | output reg out_write_reg = 1'b0, |
| 46 | output reg [3:0] out_write_num = 4'bxxxx, |
| 47 | output reg [31:0] out_write_data = 32'hxxxxxxxx, |
| 48 | output reg [31:0] out_spsr = 32'hxxxxxxxx, |
| 49 | output reg [31:0] out_cpsr = 32'hxxxxxxxx |
| 50 | ); |
| 51 | |
| 52 | reg [31:0] addr, raddr, prev_raddr, next_regdata, next_outcpsr; |
| 53 | reg [3:0] next_regsel, cur_reg, prev_reg; |
| 54 | reg next_writeback; |
| 55 | reg [31:0] align_s1, align_s2, align_rddata; |
| 56 | |
| 57 | wire next_outbubble; |
| 58 | wire next_write_reg; |
| 59 | wire [3:0] next_write_num; |
| 60 | wire [31:0] next_write_data; |
| 61 | |
| 62 | reg [1:0] lsr_state = 2'b01, next_lsr_state; |
| 63 | |
| 64 | reg [15:0] regs, next_regs; |
| 65 | reg [2:0] lsm_state = 3'b001, next_lsm_state; |
| 66 | reg [5:0] offset, prev_offset, offset_sel; |
| 67 | |
| 68 | reg [31:0] swp_oldval, next_swp_oldval; |
| 69 | reg [1:0] swp_state = 2'b01, next_swp_state; |
| 70 | |
| 71 | always @(posedge clk) |
| 72 | begin |
| 73 | outpc <= pc; |
| 74 | outinsn <= insn; |
| 75 | outbubble <= next_outbubble; |
| 76 | out_write_reg <= next_write_reg; |
| 77 | out_write_num <= next_write_num; |
| 78 | out_write_data <= next_write_data; |
| 79 | regs <= next_regs; |
| 80 | prev_reg <= cur_reg; |
| 81 | prev_offset <= offset; |
| 82 | prev_raddr <= raddr; |
| 83 | out_cpsr <= next_outcpsr; |
| 84 | out_spsr <= spsr; |
| 85 | swp_state <= next_swp_state; |
| 86 | end |
| 87 | |
| 88 | always @(*) |
| 89 | begin |
| 90 | addr = 32'hxxxxxxxx; |
| 91 | raddr = 32'hxxxxxxxx; |
| 92 | rd_req = 1'b0; |
| 93 | wr_req = 1'b0; |
| 94 | wr_data = 32'hxxxxxxxx; |
| 95 | busaddr = 32'hxxxxxxxx; |
| 96 | outstall = 1'b0; |
| 97 | next_write_reg = write_reg; |
| 98 | next_write_num = write_num; |
| 99 | next_write_data = write_data; |
| 100 | next_outbubble = inbubble; |
| 101 | outstall = 1'b0; |
| 102 | next_regs = regs; |
| 103 | cp_req = 1'b0; |
| 104 | cp_rnw = 1'bx; |
| 105 | cp_write = 32'hxxxxxxxx; |
| 106 | offset = prev_offset; |
| 107 | next_outcpsr = lsm_state == 3'b010 ? out_cpsr : cpsr; |
| 108 | next_lsm_state = lsm_state; |
| 109 | next_lsr_state = lsr_state; |
| 110 | next_swp_oldval = swp_oldval; |
| 111 | next_swp_state = swp_state; |
| 112 | cur_reg = prev_reg; |
| 113 | |
| 114 | /* XXX shit not given about endianness */ |
| 115 | /* TODO ldrh/strh */ |
| 116 | casez(insn) |
| 117 | `DECODE_ALU_SWP: if(!inbubble) begin |
| 118 | outstall = rw_wait; |
| 119 | next_outbubble = rw_wait; |
| 120 | busaddr = {op0[31:2], 2'b0}; |
| 121 | case(swp_state) |
| 122 | 2'b01: begin |
| 123 | rd_req = 1'b1; |
| 124 | outstall = 1'b1; |
| 125 | if(!rw_wait) begin |
| 126 | next_swp_state = 2'b10; |
| 127 | next_swp_oldval = rd_data; |
| 128 | end |
| 129 | end |
| 130 | 2'b10: begin |
| 131 | wr_req = 1'b1; |
| 132 | wr_data = op1; |
| 133 | next_write_reg = 1'b1; |
| 134 | next_write_num = insn[15:12]; |
| 135 | next_write_data = swp_oldval; |
| 136 | if(!rw_wait) |
| 137 | next_swp_state = 2'b01; |
| 138 | end |
| 139 | default: begin end |
| 140 | endcase |
| 141 | end |
| 142 | `DECODE_LDRSTR_UNDEFINED: begin end |
| 143 | `DECODE_LDRSTR: if(!inbubble) begin |
| 144 | next_outbubble = rw_wait; |
| 145 | outstall = rw_wait; |
| 146 | addr = insn[23] ? op0 + op1 : op0 - op1; /* up/down select */ |
| 147 | raddr = insn[24] ? op0 : addr; /* pre/post increment */ |
| 148 | busaddr = {raddr[31:2], 2'b0}; |
| 149 | /* rotate to correct position */ |
| 150 | align_s1 = raddr[1] ? {rd_data[15:0], rd_data[31:16]} : rd_data; |
| 151 | align_s2 = raddr[0] ? {align_s1[7:0], align_s1[31:8]} : align_s1; |
| 152 | /* select byte or word */ |
| 153 | align_rddata = insn[22] ? {24'b0, align_s2[7:0]} : align_s2; |
| 154 | if(!insn[20]) begin |
| 155 | wr_data = insn[22] ? {4{op2[7:0]}} : op2; /* XXX need to actually store just a byte */ |
| 156 | end |
| 157 | case(lsr_state) |
| 158 | 2'b01: begin |
| 159 | rd_req = insn[20]; |
| 160 | wr_req = ~insn[20]; |
| 161 | if(insn[20]) begin |
| 162 | next_write_reg = 1'b1; |
| 163 | next_write_num = insn[15:12]; |
| 164 | next_write_data = align_rddata; |
| 165 | end |
| 166 | if(insn[21]) begin |
| 167 | outstall = 1'b1; |
| 168 | if(!rw_wait) |
| 169 | next_lsr_state = 2'b10; |
| 170 | end |
| 171 | end |
| 172 | 2'b10: begin |
| 173 | next_write_reg = 1'b1; |
| 174 | next_write_num = insn[19:16]; |
| 175 | next_write_data = addr; |
| 176 | next_lsr_state = 2'b10; |
| 177 | end |
| 178 | default: begin end |
| 179 | endcase |
| 180 | end |
| 181 | /* XXX ldm/stm incorrect in that stupid case where one of the listed regs is the base reg */ |
| 182 | `DECODE_LDMSTM: if(!inbubble) begin |
| 183 | outstall = rw_wait; |
| 184 | next_outbubble = rw_wait; |
| 185 | case(lsm_state) |
| 186 | 3'b001: begin |
| 187 | // next_regs = insn[23] ? op1[15:0] : op1[0:15]; |
| 188 | /** verilator can suck my dick */ |
| 189 | next_regs = insn[23] ? op1[15:0] : {op1[0], op1[1], op1[2], op1[3], op1[4], op1[5], op1[6], op1[7], |
| 190 | op1[8], op1[9], op1[10], op1[11], op1[12], op1[13], op1[14], op1[15]}; |
| 191 | offset = 6'b0; |
| 192 | outstall = 1'b1; |
| 193 | next_lsm_state = 3'b010; |
| 194 | end |
| 195 | 3'b010: begin |
| 196 | rd_req = insn[20]; |
| 197 | wr_req = ~insn[20]; |
| 198 | casez(regs) |
| 199 | 16'b???????????????1: begin |
| 200 | cur_reg = 4'h0; |
| 201 | next_regs = {regs[15:1], 1'b0}; |
| 202 | end |
| 203 | 16'b??????????????10: begin |
| 204 | cur_reg = 4'h1; |
| 205 | next_regs = {regs[15:2], 2'b0}; |
| 206 | end |
| 207 | 16'b?????????????100: begin |
| 208 | cur_reg = 4'h2; |
| 209 | next_regs = {regs[15:3], 3'b0}; |
| 210 | end |
| 211 | 16'b????????????1000: begin |
| 212 | cur_reg = 4'h3; |
| 213 | next_regs = {regs[15:4], 4'b0}; |
| 214 | end |
| 215 | 16'b???????????10000: begin |
| 216 | cur_reg = 4'h4; |
| 217 | next_regs = {regs[15:5], 5'b0}; |
| 218 | end |
| 219 | 16'b??????????100000: begin |
| 220 | cur_reg = 4'h5; |
| 221 | next_regs = {regs[15:6], 6'b0}; |
| 222 | end |
| 223 | 16'b?????????1000000: begin |
| 224 | cur_reg = 4'h6; |
| 225 | next_regs = {regs[15:7], 7'b0}; |
| 226 | end |
| 227 | 16'b????????10000000: begin |
| 228 | cur_reg = 4'h7; |
| 229 | next_regs = {regs[15:8], 8'b0}; |
| 230 | end |
| 231 | 16'b???????100000000: begin |
| 232 | cur_reg = 4'h8; |
| 233 | next_regs = {regs[15:9], 9'b0}; |
| 234 | end |
| 235 | 16'b??????1000000000: begin |
| 236 | cur_reg = 4'h9; |
| 237 | next_regs = {regs[15:10], 10'b0}; |
| 238 | end |
| 239 | 16'b?????10000000000: begin |
| 240 | cur_reg = 4'hA; |
| 241 | next_regs = {regs[15:11], 11'b0}; |
| 242 | end |
| 243 | 16'b????100000000000: begin |
| 244 | cur_reg = 4'hB; |
| 245 | next_regs = {regs[15:12], 12'b0}; |
| 246 | end |
| 247 | 16'b???1000000000000: begin |
| 248 | cur_reg = 4'hC; |
| 249 | next_regs = {regs[15:13], 13'b0}; |
| 250 | end |
| 251 | 16'b??10000000000000: begin |
| 252 | cur_reg = 4'hD; |
| 253 | next_regs = {regs[15:14], 14'b0}; |
| 254 | end |
| 255 | 16'b?100000000000000: begin |
| 256 | cur_reg = 4'hE; |
| 257 | next_regs = {regs[15], 15'b0}; |
| 258 | end |
| 259 | 16'b1000000000000000: begin |
| 260 | cur_reg = 4'hF; |
| 261 | next_regs = 16'b0; |
| 262 | end |
| 263 | default: begin |
| 264 | cur_reg = 4'hx; |
| 265 | next_regs = 16'b0; |
| 266 | end |
| 267 | endcase |
| 268 | cur_reg = insn[23] ? 4'hF - cur_reg : cur_reg; |
| 269 | if(cur_reg == 4'hF && insn[22]) begin |
| 270 | next_outcpsr = spsr; |
| 271 | end |
| 272 | |
| 273 | if(rw_wait) begin |
| 274 | next_regs = regs; |
| 275 | cur_reg = prev_reg; |
| 276 | raddr = prev_raddr; |
| 277 | end |
| 278 | else begin |
| 279 | offset = prev_offset + 6'h4; |
| 280 | offset_sel = insn[24] ? offset : prev_offset; |
| 281 | raddr = insn[23] ? op0 + {26'b0, offset_sel} : op0 - {26'b0, offset_sel}; |
| 282 | if(insn[20]) begin |
| 283 | next_write_reg = 1'b1; |
| 284 | next_write_num = cur_reg; |
| 285 | next_write_data = rd_data; |
| 286 | end |
| 287 | end |
| 288 | |
| 289 | st_read = cur_reg; |
| 290 | wr_data = st_data; |
| 291 | busaddr = {raddr[31:2], 2'b0}; |
| 292 | |
| 293 | outstall = 1'b1; |
| 294 | |
| 295 | if(next_regs == 16'b0) begin |
| 296 | next_lsm_state = 3'b100; |
| 297 | end |
| 298 | end |
| 299 | 3'b100: begin |
| 300 | next_write_reg = 1'b1; |
| 301 | next_write_num = insn[19:16]; |
| 302 | next_write_data = insn[23] ? op0 + {26'b0, prev_offset} : op0 - {26'b0, prev_offset}; |
| 303 | next_lsm_state = 3'b001; |
| 304 | end |
| 305 | default: begin end |
| 306 | endcase |
| 307 | end |
| 308 | `DECODE_LDCSTC: if(!inbubble) begin |
| 309 | $display("WARNING: Unimplemented LDCSTC"); |
| 310 | end |
| 311 | `DECODE_CDP: if(!inbubble) begin |
| 312 | cp_req = 1; |
| 313 | if (cp_busy) begin |
| 314 | outstall = 1; |
| 315 | next_outbubble = 1; |
| 316 | end |
| 317 | if (!cp_ack) begin |
| 318 | /* XXX undefined instruction trap */ |
| 319 | $display("WARNING: Possible CDP undefined instruction"); |
| 320 | end |
| 321 | end |
| 322 | `DECODE_MRCMCR: if(!inbubble) begin |
| 323 | cp_req = 1; |
| 324 | cp_rnw = insn[20] /* L */; |
| 325 | if (insn[20] == 0 /* store to coprocessor */) |
| 326 | cp_write = op0; |
| 327 | else begin |
| 328 | if (insn[15:12] != 4'hF /* Fuck you ARM */) begin |
| 329 | next_write_reg = 1'b1; |
| 330 | next_write_num = insn[15:12]; |
| 331 | next_write_data = cp_read; |
| 332 | end else |
| 333 | next_outcpsr = {cp_read[31:28], cpsr[27:0]}; |
| 334 | end |
| 335 | if (cp_busy) begin |
| 336 | outstall = 1; |
| 337 | next_outbubble = 1; |
| 338 | end |
| 339 | if (!cp_ack) begin |
| 340 | $display("WARNING: Possible MRCMCR undefined instruction"); |
| 341 | end |
| 342 | end |
| 343 | default: begin end |
| 344 | endcase |
| 345 | end |
| 346 | endmodule |