1 //! Contains the RISC-V instruction encoding logic. 2 //! 3 //! These formats are specified in the RISC-V specification in section 2.2. 4 //! See: <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> 5 //! 6 //! Some instructions especially in extensions have slight variations from 7 //! the base RISC-V specification. 8 9 use super::*; 10 use crate::isa::riscv64::lower::isle::generated_code::{ 11 COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, ClOp, CrOp, CsOp, CssOp, CsznOp, FpuOPWidth, 12 VecAluOpRImm5, VecAluOpRR, VecAluOpRRRImm5, VecAluOpRRRR, VecOpCategory, ZcbMemOp, 13 }; 14 use crate::machinst::isle::WritableReg; 15 16 fn unsigned_field_width(value: u32, width: u8) -> u32 { 17 debug_assert_eq!(value & (!0 << width), 0); 18 value 19 } 20 21 /// Layout: 22 /// 0-------6-7-------11-12------14-15------19-20------24-25-------31 23 /// | Opcode | rd | funct3 | rs1 | rs2 | funct7 | 24 fn encode_r_type_bits(opcode: u32, rd: u32, funct3: u32, rs1: u32, rs2: u32, funct7: u32) -> u32 { 25 let mut bits = 0; 26 bits |= unsigned_field_width(opcode, 7); 27 bits |= unsigned_field_width(rd, 5) << 7; 28 bits |= unsigned_field_width(funct3, 3) << 12; 29 bits |= unsigned_field_width(rs1, 5) << 15; 30 bits |= unsigned_field_width(rs2, 5) << 20; 31 bits |= unsigned_field_width(funct7, 7) << 25; 32 bits 33 } 34 35 /// Encode an R-type instruction. 36 pub fn encode_r_type( 37 opcode: u32, 38 rd: WritableReg, 39 funct3: u32, 40 rs1: Reg, 41 rs2: Reg, 42 funct7: u32, 43 ) -> u32 { 44 encode_r_type_bits( 45 opcode, 46 reg_to_gpr_num(rd.to_reg()), 47 funct3, 48 reg_to_gpr_num(rs1), 49 reg_to_gpr_num(rs2), 50 funct7, 51 ) 52 } 53 54 /// Layout: 55 /// 0-------6-7-------11-12------14-15------19-20------------------31 56 /// | Opcode | rd | width | rs1 | Offset[11:0] | 57 fn encode_i_type_bits(opcode: u32, rd: u32, funct3: u32, rs1: u32, offset: u32) -> u32 { 58 let mut bits = 0; 59 bits |= unsigned_field_width(opcode, 7); 60 bits |= unsigned_field_width(rd, 5) << 7; 61 bits |= unsigned_field_width(funct3, 3) << 12; 62 bits |= unsigned_field_width(rs1, 5) << 15; 63 bits |= unsigned_field_width(offset, 12) << 20; 64 bits 65 } 66 67 /// Encode an I-type instruction. 68 pub fn encode_i_type(opcode: u32, rd: WritableReg, width: u32, rs1: Reg, offset: Imm12) -> u32 { 69 encode_i_type_bits( 70 opcode, 71 reg_to_gpr_num(rd.to_reg()), 72 width, 73 reg_to_gpr_num(rs1), 74 offset.bits(), 75 ) 76 } 77 78 /// Encode an S-type instruction. 79 /// 80 /// Layout: 81 /// 0-------6-7-------11-12------14-15------19-20---24-25-------------31 82 /// | Opcode | imm[4:0] | width | base | src | imm[11:5] | 83 pub fn encode_s_type(opcode: u32, width: u32, base: Reg, src: Reg, offset: Imm12) -> u32 { 84 let mut bits = 0; 85 bits |= unsigned_field_width(opcode, 7); 86 bits |= (offset.bits() & 0b11111) << 7; 87 bits |= unsigned_field_width(width, 3) << 12; 88 bits |= reg_to_gpr_num(base) << 15; 89 bits |= reg_to_gpr_num(src) << 20; 90 bits |= unsigned_field_width(offset.bits() >> 5, 7) << 25; 91 bits 92 } 93 94 /// Encodes a Vector ALU instruction. 95 /// 96 /// Fields: 97 /// - opcode (7 bits) 98 /// - vd (5 bits) 99 /// - funct3 (3 bits) 100 /// - vs1 (5 bits) 101 /// - vs2 (5 bits) 102 /// - vm (1 bit) 103 /// - funct6 (6 bits) 104 /// 105 /// See: https://github.com/riscv/riscv-v-spec/blob/master/valu-format.adoc 106 pub fn encode_valu( 107 op: VecAluOpRRR, 108 vd: WritableReg, 109 vs1: Reg, 110 vs2: Reg, 111 masking: VecOpMasking, 112 ) -> u32 { 113 let funct7 = (op.funct6() << 1) | masking.encode(); 114 encode_r_type_bits( 115 op.opcode(), 116 reg_to_gpr_num(vd.to_reg()), 117 op.funct3(), 118 reg_to_gpr_num(vs1), 119 reg_to_gpr_num(vs2), 120 funct7, 121 ) 122 } 123 124 /// Encodes a Vector ALU+Imm instruction. 125 /// This is just a Vector ALU instruction with an immediate in the VS1 field. 126 /// 127 /// Fields: 128 /// - opcode (7 bits) 129 /// - vd (5 bits) 130 /// - funct3 (3 bits) 131 /// - imm (5 bits) 132 /// - vs2 (5 bits) 133 /// - vm (1 bit) 134 /// - funct6 (6 bits) 135 /// 136 /// See: https://github.com/riscv/riscv-v-spec/blob/master/valu-format.adoc 137 pub fn encode_valu_rr_imm( 138 op: VecAluOpRRImm5, 139 vd: WritableReg, 140 imm: Imm5, 141 vs2: Reg, 142 masking: VecOpMasking, 143 ) -> u32 { 144 let funct7 = (op.funct6() << 1) | masking.encode(); 145 let imm = imm.bits() as u32; 146 encode_r_type_bits( 147 op.opcode(), 148 reg_to_gpr_num(vd.to_reg()), 149 op.funct3(), 150 imm, 151 reg_to_gpr_num(vs2), 152 funct7, 153 ) 154 } 155 156 pub fn encode_valu_rrrr( 157 op: VecAluOpRRRR, 158 vd: WritableReg, 159 vs2: Reg, 160 vs1: Reg, 161 masking: VecOpMasking, 162 ) -> u32 { 163 let funct7 = (op.funct6() << 1) | masking.encode(); 164 encode_r_type_bits( 165 op.opcode(), 166 reg_to_gpr_num(vd.to_reg()), 167 op.funct3(), 168 reg_to_gpr_num(vs1), 169 reg_to_gpr_num(vs2), 170 funct7, 171 ) 172 } 173 174 pub fn encode_valu_rrr_imm( 175 op: VecAluOpRRRImm5, 176 vd: WritableReg, 177 imm: Imm5, 178 vs2: Reg, 179 masking: VecOpMasking, 180 ) -> u32 { 181 let funct7 = (op.funct6() << 1) | masking.encode(); 182 let imm = imm.bits() as u32; 183 encode_r_type_bits( 184 op.opcode(), 185 reg_to_gpr_num(vd.to_reg()), 186 op.funct3(), 187 imm, 188 reg_to_gpr_num(vs2), 189 funct7, 190 ) 191 } 192 193 pub fn encode_valu_rr(op: VecAluOpRR, vd: WritableReg, vs: Reg, masking: VecOpMasking) -> u32 { 194 let funct7 = (op.funct6() << 1) | masking.encode(); 195 196 let (vs1, vs2) = if op.vs_is_vs2_encoded() { 197 (op.aux_encoding(), reg_to_gpr_num(vs)) 198 } else { 199 (reg_to_gpr_num(vs), op.aux_encoding()) 200 }; 201 202 encode_r_type_bits( 203 op.opcode(), 204 reg_to_gpr_num(vd.to_reg()), 205 op.funct3(), 206 vs1, 207 vs2, 208 funct7, 209 ) 210 } 211 212 pub fn encode_valu_r_imm( 213 op: VecAluOpRImm5, 214 vd: WritableReg, 215 imm: Imm5, 216 masking: VecOpMasking, 217 ) -> u32 { 218 let funct7 = (op.funct6() << 1) | masking.encode(); 219 220 // This is true for this opcode, not sure if there are any other ones. 221 debug_assert_eq!(op, VecAluOpRImm5::VmvVI); 222 let vs1 = imm.bits() as u32; 223 let vs2 = op.aux_encoding(); 224 225 encode_r_type_bits( 226 op.opcode(), 227 reg_to_gpr_num(vd.to_reg()), 228 op.funct3(), 229 vs1, 230 vs2, 231 funct7, 232 ) 233 } 234 235 /// Encodes a Vector CFG Imm instruction. 236 /// 237 /// See: https://github.com/riscv/riscv-v-spec/blob/master/vcfg-format.adoc 238 // TODO: Check if this is any of the known instruction types in the spec. 239 pub fn encode_vcfg_imm(opcode: u32, rd: Reg, imm: UImm5, vtype: &VType) -> u32 { 240 let mut bits = 0; 241 bits |= unsigned_field_width(opcode, 7); 242 bits |= reg_to_gpr_num(rd) << 7; 243 bits |= VecOpCategory::OPCFG.encode() << 12; 244 bits |= unsigned_field_width(imm.bits(), 5) << 15; 245 bits |= unsigned_field_width(vtype.encode(), 10) << 20; 246 bits |= 0b11 << 30; 247 bits 248 } 249 250 /// Encodes a Vector Mem Unit Stride Load instruction. 251 /// 252 /// See: https://github.com/riscv/riscv-v-spec/blob/master/vmem-format.adoc 253 /// TODO: These instructions share opcode space with LOAD-FP and STORE-FP 254 pub fn encode_vmem_load( 255 opcode: u32, 256 vd: Reg, 257 width: VecElementWidth, 258 rs1: Reg, 259 lumop: u32, 260 masking: VecOpMasking, 261 mop: u32, 262 nf: u32, 263 ) -> u32 { 264 // Width is encoded differently to avoid a clash with the FP load/store sizes. 265 let width = match width { 266 VecElementWidth::E8 => 0b000, 267 VecElementWidth::E16 => 0b101, 268 VecElementWidth::E32 => 0b110, 269 VecElementWidth::E64 => 0b111, 270 }; 271 272 let mut bits = 0; 273 bits |= unsigned_field_width(opcode, 7); 274 bits |= reg_to_gpr_num(vd) << 7; 275 bits |= width << 12; 276 bits |= reg_to_gpr_num(rs1) << 15; 277 bits |= unsigned_field_width(lumop, 5) << 20; 278 bits |= masking.encode() << 25; 279 bits |= unsigned_field_width(mop, 2) << 26; 280 281 // The mew bit (inst[28]) when set is expected to be used to encode expanded 282 // memory sizes of 128 bits and above, but these encodings are currently reserved. 283 bits |= 0b0 << 28; 284 285 bits |= unsigned_field_width(nf, 3) << 29; 286 bits 287 } 288 289 /// Encodes a Vector Mem Unit Stride Load instruction. 290 /// 291 /// See: https://github.com/riscv/riscv-v-spec/blob/master/vmem-format.adoc 292 /// TODO: These instructions share opcode space with LOAD-FP and STORE-FP 293 pub fn encode_vmem_store( 294 opcode: u32, 295 vs3: Reg, 296 width: VecElementWidth, 297 rs1: Reg, 298 sumop: u32, 299 masking: VecOpMasking, 300 mop: u32, 301 nf: u32, 302 ) -> u32 { 303 // This is pretty much the same as the load instruction, just 304 // with different names on the fields. 305 encode_vmem_load(opcode, vs3, width, rs1, sumop, masking, mop, nf) 306 } 307 308 // The CSR Reg instruction is really just an I type instruction with the CSR in 309 // the immediate field. 310 pub fn encode_csr_reg(op: CsrRegOP, rd: WritableReg, rs: Reg, csr: CSR) -> u32 { 311 encode_i_type(op.opcode(), rd, op.funct3(), rs, csr.bits()) 312 } 313 314 // The CSR Imm instruction is an I type instruction with the CSR in 315 // the immediate field and the value to be set in the `rs1` field. 316 pub fn encode_csr_imm(op: CsrImmOP, rd: WritableReg, csr: CSR, imm: UImm5) -> u32 { 317 encode_i_type_bits( 318 op.opcode(), 319 reg_to_gpr_num(rd.to_reg()), 320 op.funct3(), 321 imm.bits(), 322 csr.bits().bits(), 323 ) 324 } 325 326 // Encode a CR type instruction. 327 // 328 // 0--1-2-----6-7-------11-12-------15 329 // |op | rs2 | rd/rs1 | funct4 | 330 pub fn encode_cr_type(op: CrOp, rd: WritableReg, rs2: Reg) -> u16 { 331 let mut bits = 0; 332 bits |= unsigned_field_width(op.op().bits(), 2); 333 bits |= reg_to_gpr_num(rs2) << 2; 334 bits |= reg_to_gpr_num(rd.to_reg()) << 7; 335 bits |= unsigned_field_width(op.funct4(), 4) << 12; 336 bits.try_into().unwrap() 337 } 338 339 // This isn't technically a instruction format that exists. It's just a CR type 340 // where the source is rs1, rs2 is zero. rs1 is never written to. 341 // 342 // Used for C.JR and C.JALR 343 pub fn encode_cr2_type(op: CrOp, rs1: Reg) -> u16 { 344 encode_cr_type(op, WritableReg::from_reg(rs1), zero_reg()) 345 } 346 347 // Encode a CA type instruction. 348 // 349 // 0--1-2-----4-5--------6-7--------9-10------15 350 // |op | rs2 | funct2 | rd/rs1 | funct6 | 351 pub fn encode_ca_type(op: CaOp, rd: WritableReg, rs2: Reg) -> u16 { 352 let mut bits = 0; 353 bits |= unsigned_field_width(op.op().bits(), 2); 354 bits |= reg_to_compressed_gpr_num(rs2) << 2; 355 bits |= unsigned_field_width(op.funct2(), 2) << 5; 356 bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7; 357 bits |= unsigned_field_width(op.funct6(), 6) << 10; 358 bits.try_into().unwrap() 359 } 360 361 // Encode a CJ type instruction. 362 // 363 // The imm field is a 11 bit signed immediate that is shifted left by 1. 364 // 365 // 0--1-2-----12-13--------15 366 // |op | imm | funct3 | 367 pub fn encode_cj_type(op: CjOp, imm: Imm12) -> u16 { 368 let imm = imm.bits(); 369 debug_assert!(imm & 1 == 0); 370 371 // The offset bits are in rather weird positions. 372 // [11|4|9:8|10|6|7|3:1|5] 373 let mut imm_field = 0; 374 imm_field |= ((imm >> 11) & 1) << 10; 375 imm_field |= ((imm >> 4) & 1) << 9; 376 imm_field |= ((imm >> 8) & 3) << 7; 377 imm_field |= ((imm >> 10) & 1) << 6; 378 imm_field |= ((imm >> 6) & 1) << 5; 379 imm_field |= ((imm >> 7) & 1) << 4; 380 imm_field |= ((imm >> 1) & 7) << 1; 381 imm_field |= ((imm >> 5) & 1) << 0; 382 383 let mut bits = 0; 384 bits |= unsigned_field_width(op.op().bits(), 2); 385 bits |= unsigned_field_width(imm_field, 11) << 2; 386 bits |= unsigned_field_width(op.funct3(), 3) << 13; 387 bits.try_into().unwrap() 388 } 389 390 // Encode a CI type instruction. 391 // 392 // The imm field is a 6 bit signed immediate. 393 // 394 // 0--1-2-------6-7-------11-12-----12-13-----15 395 // |op | imm[4:0] | src | imm[5] | funct3 | 396 pub fn encode_ci_type(op: CiOp, rd: WritableReg, imm: Imm6) -> u16 { 397 let imm = imm.bits(); 398 399 let mut bits = 0; 400 bits |= unsigned_field_width(op.op().bits(), 2); 401 bits |= unsigned_field_width((imm & 0x1f) as u32, 5) << 2; 402 bits |= reg_to_gpr_num(rd.to_reg()) << 7; 403 bits |= unsigned_field_width(((imm >> 5) & 1) as u32, 1) << 12; 404 bits |= unsigned_field_width(op.funct3(), 3) << 13; 405 bits.try_into().unwrap() 406 } 407 408 // Stack-Pointer relative loads are regular CI instructions, but, the immediate 409 // is zero extended, and with a slightly different immediate field encoding. 410 pub fn encode_ci_sp_load(op: CiOp, rd: WritableReg, imm: Uimm6) -> u16 { 411 let imm = imm.bits(); 412 413 // These are the spec encoded offsets. 414 // LWSP: [5|4:2|7:6] 415 // LDSP: [5|4:3|8:6] 416 // FLDSP: [5|4:3|8:6] 417 // 418 // We don't receive the entire offset in `imm`, just a multiple of the load-size. 419 420 // Number of bits in the lowest position of imm. 3 for lwsp, 2 for {f,}ldsp. 421 let low_bits = match op { 422 CiOp::CLwsp => 3, // [4:2] 423 CiOp::CLdsp | CiOp::CFldsp => 2, // [4:3] 424 _ => unreachable!(), 425 }; 426 let high_bits = 6 - 1 - low_bits; 427 let mut enc_imm = 0; 428 429 // Encode [7:6] at the bottom of imm 430 enc_imm |= imm >> (6 - high_bits); 431 432 // Next place [4:2] in the middle 433 enc_imm |= (imm & ((1 << low_bits) - 1)) << high_bits; 434 435 // Finally place [5] at the top 436 enc_imm |= ((imm >> low_bits) & 1) << 5; 437 438 let enc_imm = Imm6::maybe_from_i16((enc_imm as i16) << 10 >> 10).unwrap(); 439 440 encode_ci_type(op, rd, enc_imm) 441 } 442 443 /// c.addi16sp is a regular CI op, but the immediate field is encoded in a weird way 444 pub fn encode_c_addi16sp(imm: Imm6) -> u16 { 445 let imm = imm.bits(); 446 447 // [6|1|3|5:4|2] 448 let mut enc_imm = 0; 449 enc_imm |= ((imm >> 5) & 1) << 5; 450 enc_imm |= ((imm >> 0) & 1) << 4; 451 enc_imm |= ((imm >> 2) & 1) << 3; 452 enc_imm |= ((imm >> 3) & 3) << 1; 453 enc_imm |= ((imm >> 1) & 1) << 0; 454 let enc_imm = Imm6::maybe_from_i16((enc_imm as i16) << 10 >> 10).unwrap(); 455 456 encode_ci_type(CiOp::CAddi16sp, writable_stack_reg(), enc_imm) 457 } 458 459 // Encode a CIW type instruction. 460 // 461 // 0--1-2------4-5------12-13--------15 462 // |op | rd | imm | funct3 | 463 pub fn encode_ciw_type(op: CiwOp, rd: WritableReg, imm: u8) -> u16 { 464 // [3:2|7:4|0|1] 465 let mut imm_field = 0; 466 imm_field |= ((imm >> 1) & 1) << 0; 467 imm_field |= ((imm >> 0) & 1) << 1; 468 imm_field |= ((imm >> 4) & 15) << 2; 469 imm_field |= ((imm >> 2) & 3) << 6; 470 471 let mut bits = 0; 472 bits |= unsigned_field_width(op.op().bits(), 2); 473 bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 2; 474 bits |= unsigned_field_width(imm_field as u32, 8) << 5; 475 bits |= unsigned_field_width(op.funct3(), 3) << 13; 476 bits.try_into().unwrap() 477 } 478 479 // Encode a CB type instruction. 480 // 481 // The imm field is a 6 bit signed immediate. 482 // 483 // 0--1-2-------6-7-------9-10-------11-12-------13--------15 484 // |op | imm[4:0] | dst | funct2 | imm[5] | funct3 | 485 pub fn encode_cb_type(op: CbOp, rd: WritableReg, imm: Imm6) -> u16 { 486 let imm = imm.bits(); 487 488 let mut bits = 0; 489 bits |= unsigned_field_width(op.op().bits(), 2); 490 bits |= unsigned_field_width((imm & 0x1f) as u32, 5) << 2; 491 bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7; 492 bits |= unsigned_field_width(op.funct2(), 2) << 10; 493 bits |= unsigned_field_width(((imm >> 5) & 1) as u32, 1) << 12; 494 bits |= unsigned_field_width(op.funct3(), 3) << 13; 495 bits.try_into().unwrap() 496 } 497 498 // Encode a CSS type instruction. 499 // 500 // The imm field is a 6 bit unsigned immediate. 501 // 502 // 0--1-2-------6-7--------12-13-------15 503 // |op | src | imm | funct3 | 504 pub fn encode_css_type(op: CssOp, src: Reg, imm: Uimm6) -> u16 { 505 let imm = imm.bits(); 506 507 // These are the spec encoded offsets. 508 // c.swsp: [5:2|7:6] 509 // c.sdsp: [5:3|8:6] 510 // c.fsdsp: [5:3|8:6] 511 // 512 // We don't receive the entire offset in `imm`, just a multiple of the load-size. 513 514 // Number of bits in the lowest position of imm. 4 for c.swsp, 3 for c.{f,}sdsp. 515 let low_bits = match op { 516 CssOp::CSwsp => 4, // [5:2] 517 CssOp::CSdsp | CssOp::CFsdsp => 3, // [5:3] 518 }; 519 let high_bits = 6 - low_bits; 520 521 let mut enc_imm = 0; 522 enc_imm |= (imm & ((1 << low_bits) - 1)) << high_bits; 523 enc_imm |= imm >> low_bits; 524 525 let mut bits = 0; 526 bits |= unsigned_field_width(op.op().bits(), 2); 527 bits |= reg_to_gpr_num(src) << 2; 528 bits |= unsigned_field_width(enc_imm as u32, 6) << 7; 529 bits |= unsigned_field_width(op.funct3(), 3) << 13; 530 bits.try_into().unwrap() 531 } 532 533 // Encode a CS type instruction. 534 // 535 // The imm field is a 5 bit unsigned immediate. 536 // 537 // 0--1-2-----4-5----------6-7---------9-10----------12-13-----15 538 // |op | src | imm(2-bit) | base | imm(3-bit) | funct3 | 539 pub fn encode_cs_type(op: CsOp, src: Reg, base: Reg, imm: Uimm5) -> u16 { 540 let size = match op { 541 CsOp::CFsd | CsOp::CSd => 8, 542 CsOp::CSw => 4, 543 }; 544 545 encode_cs_cl_type_bits(op.op(), op.funct3(), size, src, base, imm) 546 } 547 548 // Encode a CL type instruction. 549 // 550 // The imm field is a 5 bit unsigned immediate. 551 // 552 // 0--1-2------4-5----------6-7---------9-10----------12-13-----15 553 // |op | dest | imm(2-bit) | base | imm(3-bit) | funct3 | 554 pub fn encode_cl_type(op: ClOp, dest: WritableReg, base: Reg, imm: Uimm5) -> u16 { 555 let size = match op { 556 ClOp::CFld | ClOp::CLd => 8, 557 ClOp::CLw => 4, 558 }; 559 560 encode_cs_cl_type_bits(op.op(), op.funct3(), size, dest.to_reg(), base, imm) 561 } 562 563 // CL and CS type instructions have the same physical layout. 564 // 565 // 0--1-2----------4-5----------6-7---------9-10----------12-13-----15 566 // |op | dest/src | imm(2-bit) | base | imm(3-bit) | funct3 | 567 fn encode_cs_cl_type_bits( 568 op: COpcodeSpace, 569 funct3: u32, 570 size: u32, 571 dest_src: Reg, 572 base: Reg, 573 imm: Uimm5, 574 ) -> u16 { 575 let imm = imm.bits(); 576 577 // c.sw / c.lw: [2|6] 578 // c.sd / c.ld: [7:6] 579 // c.fsd / c.fld: [7:6] 580 // 581 // We differentiate these based on the operation size 582 let imm2 = match size { 583 4 => ((imm >> 4) & 1) | ((imm & 1) << 1), 584 8 => (imm >> 3) & 0b11, 585 _ => unreachable!(), 586 }; 587 588 // [5:3] on all opcodes 589 let imm3 = match size { 590 4 => (imm >> 1) & 0b111, 591 8 => (imm >> 0) & 0b111, 592 _ => unreachable!(), 593 }; 594 595 let mut bits = 0; 596 bits |= unsigned_field_width(op.bits(), 2); 597 bits |= reg_to_compressed_gpr_num(dest_src) << 2; 598 bits |= unsigned_field_width(imm2 as u32, 2) << 5; 599 bits |= reg_to_compressed_gpr_num(base) << 7; 600 bits |= unsigned_field_width(imm3 as u32, 3) << 10; 601 bits |= unsigned_field_width(funct3, 3) << 13; 602 bits.try_into().unwrap() 603 } 604 605 // Encode a CSZN type instruction. 606 // 607 // This is an additional encoding format that is introduced in the Zcb extension. 608 // 609 // 0--1-2---------6-7--------9-10------15 610 // |op | funct5 | rd/rs1 | funct6 | 611 pub fn encode_cszn_type(op: CsznOp, rd: WritableReg) -> u16 { 612 let mut bits = 0; 613 bits |= unsigned_field_width(op.op().bits(), 2); 614 bits |= unsigned_field_width(op.funct5(), 5) << 2; 615 bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7; 616 bits |= unsigned_field_width(op.funct6(), 6) << 10; 617 bits.try_into().unwrap() 618 } 619 620 // Encodes the various memory operations in the Zcb extension. 621 // 622 // 0--1-2----------4-5----------6-7---------9-10-------15 623 // |op | dest/src | imm(2-bit) | base | funct6 | 624 fn encode_zcbmem_bits(op: ZcbMemOp, dest_src: Reg, base: Reg, imm: Uimm2) -> u16 { 625 let imm = imm.bits(); 626 627 // For these ops, bit 6 is part of the opcode, and bit 5 encodes the imm offset. 628 let imm = match op { 629 ZcbMemOp::CLh | ZcbMemOp::CLhu | ZcbMemOp::CSh => { 630 debug_assert_eq!(imm & !1, 0); 631 // Only c.lh has this bit as 1 632 let opcode_bit = (op == ZcbMemOp::CLh) as u8; 633 imm | (opcode_bit << 1) 634 } 635 // In the rest of the ops the imm is reversed. 636 _ => ((imm & 1) << 1) | ((imm >> 1) & 1), 637 }; 638 639 let mut bits = 0; 640 bits |= unsigned_field_width(op.op().bits(), 2); 641 bits |= reg_to_compressed_gpr_num(dest_src) << 2; 642 bits |= unsigned_field_width(imm as u32, 2) << 5; 643 bits |= reg_to_compressed_gpr_num(base) << 7; 644 bits |= unsigned_field_width(op.funct6(), 6) << 10; 645 bits.try_into().unwrap() 646 } 647 648 pub fn encode_zcbmem_load(op: ZcbMemOp, rd: WritableReg, base: Reg, imm: Uimm2) -> u16 { 649 encode_zcbmem_bits(op, rd.to_reg(), base, imm) 650 } 651 652 pub fn encode_zcbmem_store(op: ZcbMemOp, src: Reg, base: Reg, imm: Uimm2) -> u16 { 653 encode_zcbmem_bits(op, src, base, imm) 654 } 655 656 pub fn encode_fli(width: FpuOPWidth, imm: FliConstant, rd: WritableReg) -> u32 { 657 // FLI.{H,S,D} is encoded as a FMV.{H,W,D} instruction with rs2 set to the 658 // immediate value to be loaded. 659 let op = FpuOPRR::FmvFmtX; 660 let frm = 0; // FRM is hard coded to 0 in both instructions 661 let rs2 = 1; // rs2 set to 1 is what differentiates FLI from FMV 662 663 let mut bits = 0; 664 bits |= unsigned_field_width(op.opcode(), 7); 665 bits |= reg_to_gpr_num(rd.to_reg()) << 7; 666 bits |= unsigned_field_width(frm, 3) << 12; 667 bits |= unsigned_field_width(imm.bits() as u32, 5) << 15; 668 bits |= unsigned_field_width(rs2, 6) << 20; 669 bits |= unsigned_field_width(op.funct7(width), 7) << 25; 670 bits 671 } 672 673 pub fn encode_fp_rr(op: FpuOPRR, width: FpuOPWidth, frm: FRM, rd: WritableReg, rs: Reg) -> u32 { 674 encode_r_type_bits( 675 op.opcode(), 676 reg_to_gpr_num(rd.to_reg()), 677 frm.as_u32(), 678 reg_to_gpr_num(rs), 679 op.rs2(), 680 op.funct7(width), 681 ) 682 } 683 684 pub fn encode_fp_rrr( 685 op: FpuOPRRR, 686 width: FpuOPWidth, 687 frm: FRM, 688 rd: WritableReg, 689 rs1: Reg, 690 rs2: Reg, 691 ) -> u32 { 692 encode_r_type_bits( 693 op.opcode(), 694 reg_to_gpr_num(rd.to_reg()), 695 frm.as_u32(), 696 reg_to_gpr_num(rs1), 697 reg_to_gpr_num(rs2), 698 op.funct7(width), 699 ) 700 } 701 702 pub fn encode_fp_rrrr( 703 op: FpuOPRRRR, 704 width: FpuOPWidth, 705 frm: FRM, 706 rd: WritableReg, 707 rs1: Reg, 708 rs2: Reg, 709 rs3: Reg, 710 ) -> u32 { 711 let funct7 = (reg_to_gpr_num(rs3) << 2) | width.as_u32(); 712 encode_r_type_bits( 713 op.opcode(), 714 reg_to_gpr_num(rd.to_reg()), 715 frm.as_u32(), 716 reg_to_gpr_num(rs1), 717 reg_to_gpr_num(rs2), 718 funct7, 719 ) 720 } 721