From 2f60c83d10543c4cc7ecce995bc2c969984e65ed Mon Sep 17 00:00:00 2001 From: Tim Hutt Date: Tue, 5 Mar 2024 13:44:32 +0000 Subject: [PATCH] Implement Zcb extension This adds an implementation of the Zcb code size extension. Co-authored-by: Martin Berger --- LICENCE | 2 +- Makefile | 2 + c_emulator/riscv_platform.c | 5 + c_emulator/riscv_platform_impl.c | 1 + c_emulator/riscv_sim.c | 6 + model/riscv_insts_zcb.sail | 210 ++++++++++++++++++++++++++++++ model/riscv_sys_regs.sail | 5 + ocaml_emulator/platform.ml | 2 + ocaml_emulator/riscv_ocaml_sim.ml | 3 + 9 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 model/riscv_insts_zcb.sail diff --git a/LICENCE b/LICENCE index 9dddf34e0..432b78d79 100644 --- a/LICENCE +++ b/LICENCE @@ -16,7 +16,7 @@ Copyright (c) 2017-2024 Brian Campbell Chris Casinghino Christopher Pulte - Codasip, for contributions by Tim Hutt + Codasip, for contributions by Tim Hutt, Martin Berger and Ben Fletcher dylux eroom1966 Google LLC, for contributions by its employees diff --git a/Makefile b/Makefile index 255224125..6ac9d2926 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,8 @@ SAIL_DEFAULT_INST += riscv_insts_zbb.sail SAIL_DEFAULT_INST += riscv_insts_zbc.sail SAIL_DEFAULT_INST += riscv_insts_zbs.sail +SAIL_DEFAULT_INST += riscv_insts_zcb.sail + SAIL_DEFAULT_INST += riscv_insts_zfh.sail # Zfa needs to be added after fext, dext and Zfh (as it needs # definitions from those) diff --git a/c_emulator/riscv_platform.c b/c_emulator/riscv_platform.c index 253da353f..6544de65c 100644 --- a/c_emulator/riscv_platform.c +++ b/c_emulator/riscv_platform.c @@ -32,6 +32,11 @@ bool sys_enable_fdext(unit u) return rv_enable_fdext; } +bool sys_enable_zcb(unit u) +{ + return rv_enable_zcb; +} + bool sys_enable_zfinx(unit u) { return rv_enable_zfinx; diff --git a/c_emulator/riscv_platform_impl.c b/c_emulator/riscv_platform_impl.c index c2ae256ef..449bb1df7 100644 --- a/c_emulator/riscv_platform_impl.c +++ b/c_emulator/riscv_platform_impl.c @@ -6,6 +6,7 @@ uint64_t rv_pmp_count = 0; uint64_t rv_pmp_grain = 0; +bool rv_enable_zcb = false; bool rv_enable_zfinx = false; bool rv_enable_rvc = true; bool rv_enable_next = false; diff --git a/c_emulator/riscv_sim.c b/c_emulator/riscv_sim.c index f3c63435b..ec5acf16b 100644 --- a/c_emulator/riscv_sim.c +++ b/c_emulator/riscv_sim.c @@ -53,6 +53,7 @@ const char *RV32ISA = "RV32IMAC"; #define OPT_ENABLE_WRITABLE_FIOM 1001 #define OPT_PMP_COUNT 1002 #define OPT_PMP_GRAIN 1003 +#define OPT_ENABLE_ZCB 10014 static bool do_dump_dts = false; static bool do_show_times = false; @@ -145,6 +146,7 @@ static struct option options[] = { {"inst-limit", required_argument, 0, 'l' }, {"enable-zfinx", no_argument, 0, 'x' }, {"enable-writable-fiom", no_argument, 0, OPT_ENABLE_WRITABLE_FIOM}, + {"enable-zcb", no_argument, 0, OPT_ENABLE_ZCB }, #ifdef SAILCOV {"sailcov-file", required_argument, 0, 'c' }, #endif @@ -386,6 +388,10 @@ static int process_args(int argc, char **argv) case 'l': insn_limit = atoi(optarg); break; + case OPT_ENABLE_ZCB: + fprintf(stderr, "enabling Zcb extension.\n"); + rv_enable_zcb = true; + break; case 'x': fprintf(stderr, "enabling Zfinx support.\n"); rv_enable_zfinx = true; diff --git a/model/riscv_insts_zcb.sail b/model/riscv_insts_zcb.sail new file mode 100644 index 000000000..4583e7a6c --- /dev/null +++ b/model/riscv_insts_zcb.sail @@ -0,0 +1,210 @@ +/*=======================================================================================*/ +/* This Sail RISC-V architecture model, comprising all files and */ +/* directories except where otherwise noted is subject the BSD */ +/* two-clause license in the LICENSE file. */ +/* */ +/* SPDX-License-Identifier: BSD-2-Clause */ +/*=======================================================================================*/ + +union clause ast = C_LBU : (bits(2), cregidx, cregidx) + +mapping clause encdec_compressed = + C_LBU(uimm1 @ uimm0, rdc, rs1c) if haveZcb() + <-> 0b100 @ 0b000 @ rs1c : cregidx @ uimm0 : bits(1) @ uimm1 : bits(1) @ rdc : cregidx @ 0b00 if haveZcb() + +mapping clause assembly = C_LBU(uimm, rdc, rs1c) <-> + "c.lbu" ^ spc() ^ creg_name(rdc) ^ sep() ^ hex_bits_2(uimm) ^ opt_spc() ^ "(" ^ opt_spc() ^ creg_name(rs1c) ^ opt_spc() ^ ")" + +function clause execute C_LBU(uimm, rdc, rs1c) = { + let imm : bits(12) = zero_extend(uimm); + let rd = creg2reg_idx(rdc); + let rs1 = creg2reg_idx(rs1c); + execute(LOAD(imm, rs1, rd, true, BYTE, false, false)) +} + +/* ****************************************************************** */ + +union clause ast = C_LHU : (bits(2), cregidx, cregidx) + +mapping clause encdec_compressed = + C_LHU(uimm1 @ 0b0, rdc, rs1c) if haveZcb() + <-> 0b100 @ 0b001 @ rs1c : cregidx @ 0b0 @ uimm1 : bits(1) @ rdc : cregidx @ 0b00 if haveZcb() + +mapping clause assembly = C_LHU(uimm, rdc, rs1c) <-> + "c.lhu" ^ spc() ^ creg_name(rdc) ^ sep() ^ hex_bits_2(uimm) ^ opt_spc() ^ "(" ^ opt_spc() ^ creg_name(rs1c) ^ opt_spc() ^ ")" + +function clause execute C_LHU(uimm, rdc, rs1c) = { + let imm : bits(12) = zero_extend(uimm); + let rd = creg2reg_idx(rdc); + let rs1 = creg2reg_idx(rs1c); + execute(LOAD(imm, rs1, rd, true, HALF, false, false)) +} + +/* ****************************************************************** */ + +union clause ast = C_LH : (bits(2), cregidx, cregidx) + +mapping clause encdec_compressed = + C_LH(uimm1 @ 0b0, rdc, rs1c) if haveZcb() + <-> 0b100 @ 0b001 @ rs1c : cregidx @ 0b1 @ uimm1 : bits(1) @ rdc : cregidx @ 0b00 if haveZcb() + +mapping clause assembly = C_LH(uimm, rdc, rs1c) <-> + "c.lh" ^ spc() ^ creg_name(rdc) ^ sep() ^ hex_bits_2(uimm) ^ opt_spc() ^ "(" ^ opt_spc() ^ creg_name(rs1c) ^ opt_spc() ^ ")" + +function clause execute C_LH(uimm, rdc, rs1c) = { + let imm : bits(12) = zero_extend(uimm); + let rd = creg2reg_idx(rdc); + let rs1 = creg2reg_idx(rs1c); + execute(LOAD(imm, rs1, rd, false, HALF, false, false)) +} + +/* ****************************************************************** */ + +union clause ast = C_SB : (bits(2), cregidx, cregidx) + +mapping clause encdec_compressed = + C_SB(uimm1 @ uimm0, rs1c, rs2c) if haveZcb() + <-> 0b100 @ 0b010 @ rs1c : cregidx @ uimm0 : bits(1) @ uimm1 : bits(1) @ rs2c @ 0b00 if haveZcb() + +mapping clause assembly = C_SB(uimm, rs1c, rs2c) <-> + "c.sb" ^ spc() ^ creg_name(rs2c) ^ sep() ^ hex_bits_2(uimm) ^ opt_spc() ^ "(" ^ opt_spc() ^ creg_name(rs1c) ^ opt_spc() ^ ")" + +function clause execute C_SB(uimm, rs1c, rs2c) = { + let imm : bits(12) = zero_extend(uimm); + let rs1 = creg2reg_idx(rs1c); + let rs2 = creg2reg_idx(rs2c); + execute(STORE(imm, rs2, rs1, BYTE, false, false)) +} + +/* ****************************************************************** */ + +union clause ast = C_SH : (bits(2), cregidx, cregidx) + +mapping clause encdec_compressed = + C_SH(uimm1 @ 0b0, rs1c, rs2c) if haveZcb() + <-> 0b100 @ 0b011 @ rs1c : cregidx @ 0b0 @ uimm1 : bits(1) @ rs2c : cregidx @ 0b00 if haveZcb() + +mapping clause assembly = C_SH(uimm, rs1c, rs2c) <-> + "c.sh" ^ spc() ^ creg_name(rs1c) ^ sep() ^ hex_bits_2(uimm) ^ opt_spc() ^ "(" ^ opt_spc() ^ creg_name(rs2c) ^ opt_spc() ^ ")" + +function clause execute C_SH(uimm, rs1c, rs2c) = { + let imm : bits(12) = zero_extend(uimm); + let rs1 = creg2reg_idx(rs1c); + let rs2 = creg2reg_idx(rs2c); + execute(STORE(imm, rs2, rs1, HALF, false, false)) +} + +/* ****************************************************************** */ + +union clause ast = C_ZEXT_B : (cregidx) + +mapping clause encdec_compressed = + C_ZEXT_B(rsdc) if haveZcb() + <-> 0b100 @ 0b111 @ rsdc : cregidx @ 0b11 @ 0b000 @ 0b01 if haveZcb() + +mapping clause assembly = C_ZEXT_B(rsdc) <-> + "c.zext.b" ^ spc() ^ creg_name(rsdc) + +function clause execute C_ZEXT_B(rsdc) = { + let rsd = creg2reg_idx(rsdc); + X(rsd) = zero_extend(X(rsd)[7..0]); + RETIRE_SUCCESS +} + +/* ****************************************************************** */ + +union clause ast = C_SEXT_B : (cregidx) + +mapping clause encdec_compressed = + C_SEXT_B(rsdc) if haveZcb() & haveZbb() + <-> 0b100 @ 0b111 @ rsdc : cregidx @ 0b11 @ 0b001 @ 0b01 if haveZcb() & haveZbb() + +mapping clause assembly = C_SEXT_B(rsdc) <-> + "c.sext.b" ^ spc() ^ creg_name(rsdc) + +function clause execute C_SEXT_B(rsdc) = { + let rsd = creg2reg_idx(rsdc); + execute(ZBB_EXTOP(rsd, rsd, RISCV_SEXTB)) +} + +/* ****************************************************************** */ + +union clause ast = C_ZEXT_H : (cregidx) + +mapping clause encdec_compressed = + C_ZEXT_H(rsdc) if haveZcb() & haveZbb() + <-> 0b100 @ 0b111 @ rsdc : cregidx @ 0b11 @ 0b010 @ 0b01 if haveZcb() & haveZbb() + +mapping clause assembly = C_ZEXT_H(rsdc) <-> + "c.zext.h" ^ spc() ^ creg_name(rsdc) + +function clause execute C_ZEXT_H(rsdc) = { + let rsd = creg2reg_idx(rsdc); + execute(ZBB_EXTOP(rsd, rsd, RISCV_ZEXTH)) +} + +/* ****************************************************************** */ + +union clause ast = C_SEXT_H : (cregidx) + +mapping clause encdec_compressed = + C_SEXT_H(rsdc) if haveZcb() & haveZbb() + <-> 0b100 @ 0b111 @ rsdc : cregidx @ 0b11 @ 0b011 @ 0b01 if haveZcb() & haveZbb() + +mapping clause assembly = C_SEXT_H(rsdc) <-> + "c.sext.h" ^ spc() ^ creg_name(rsdc) + +function clause execute C_SEXT_H(rsdc) = { + let rsd = creg2reg_idx(rsdc); + execute(ZBB_EXTOP(rsd, rsd, RISCV_SEXTH)) +} + +/* ****************************************************************** */ + +union clause ast = C_ZEXT_W : (cregidx) + +mapping clause encdec_compressed = + C_ZEXT_W(rsdc) if haveZcb() & haveZba() & sizeof(xlen) == 64 + <-> 0b100 @ 0b111 @ rsdc : cregidx @ 0b11 @ 0b100 @ 0b01 if haveZcb() & haveZba() & sizeof(xlen) == 64 + +mapping clause assembly = C_ZEXT_W(rsdc) <-> + "c.zext.w" ^ spc() ^ creg_name(rsdc) + +function clause execute C_ZEXT_W(rsdc) = { + let rsd = creg2reg_idx(rsdc); + execute (ZBA_RTYPEUW(0b00000, rsd, rsd, RISCV_ADDUW)) // Note 0b00000 is the regidx of the zero register +} + +/* ****************************************************************** */ + +union clause ast = C_NOT : (cregidx) + +mapping clause encdec_compressed = + C_NOT(rsdc) if haveZcb() + <-> 0b100 @ 0b111 @ rsdc : cregidx @ 0b11 @ 0b101 @ 0b01 if haveZcb() + +mapping clause assembly = C_NOT(rsdc) <-> + "c.not" ^ spc() ^ creg_name(rsdc) + +function clause execute C_NOT(rsdc) = { + let r = creg2reg_idx(rsdc); + X(r) = ~(X(r)); + RETIRE_SUCCESS +} + +/* ****************************************************************** */ + +union clause ast = C_MUL : (cregidx,cregidx) + +mapping clause encdec_compressed = + C_MUL(rsdc, rs2c) if haveZcb() & (haveMulDiv() | haveZmmul()) + <-> 0b100 @ 0b111 @ rsdc : cregidx @ 0b10 @ rs2c : cregidx @ 0b01 if haveZcb() & (haveMulDiv() | haveZmmul()) + +mapping clause assembly = C_MUL(rsdc, rs2c) <-> + "c.mul" ^ spc() ^ creg_name(rsdc) ^ sep() ^ creg_name(rs2c) + +function clause execute C_MUL(rsdc, rs2c) = { + let rd = creg2reg_idx(rsdc); + let rs = creg2reg_idx(rs2c); + execute(MUL(rs, rd, rd, false, true, true)) +} diff --git a/model/riscv_sys_regs.sail b/model/riscv_sys_regs.sail index fc20e4af4..b127abdee 100644 --- a/model/riscv_sys_regs.sail +++ b/model/riscv_sys_regs.sail @@ -84,6 +84,8 @@ val sys_enable_writable_misa = {c: "sys_enable_writable_misa", ocaml: "Platform. val sys_enable_rvc = {c: "sys_enable_rvc", ocaml: "Platform.enable_rvc", _: "sys_enable_rvc"} : unit -> bool /* whether misa.{f,d} were enabled at boot */ val sys_enable_fdext = {c: "sys_enable_fdext", ocaml: "Platform.enable_fdext", _: "sys_enable_fdext"} : unit -> bool +/* whether Zcb was enabled at boot */ +val sys_enable_zcb = {c: "sys_enable_zcb", ocaml: "Platform.enable_zcb", _: "sys_enable_zcb"} : unit -> bool /* whether zfinx was enabled at boot */ val sys_enable_zfinx = {c: "sys_enable_zfinx", ocaml: "Platform.enable_zfinx", _: "sys_enable_zfinx"} : unit -> bool /* whether the N extension was enabled at boot */ @@ -309,6 +311,9 @@ function haveZfh() -> bool = (misa[F] == 0b1) & (mstatus[FS] != 0b00) /* V extension has to enable both via misa.V as well as mstatus.VS */ function haveVExt() -> bool = (misa[V] == 0b1) & (mstatus[VS] != 0b00) +/* Zcb has simple code-size saving instructions. (The Zcb extension depends on the Zca extension.) */ +function haveZcb() -> bool = sys_enable_zcb() + /* Zhinx, Zfinx and Zdinx extensions (TODO: gate FCSR access on [mhs]stateen0 bit 1 when implemented) */ function haveZhinx() -> bool = sys_enable_zfinx() function haveZfinx() -> bool = sys_enable_zfinx() diff --git a/ocaml_emulator/platform.ml b/ocaml_emulator/platform.ml index a8bde44d6..2f0aaaf7c 100644 --- a/ocaml_emulator/platform.ml +++ b/ocaml_emulator/platform.ml @@ -10,6 +10,7 @@ let config_enable_writable_misa = ref true let config_enable_dirty_update = ref false let config_enable_misaligned_access = ref false let config_mtval_has_illegal_inst_bits = ref false +let config_enable_zcb = ref false let config_enable_writable_fiom = ref true let config_enable_vext = ref true let config_pmp_count = ref Big_int.zero @@ -88,6 +89,7 @@ let enable_vext () = !config_enable_vext let enable_dirty_update () = !config_enable_dirty_update let enable_misaligned_access () = !config_enable_misaligned_access let mtval_has_illegal_inst_bits () = !config_mtval_has_illegal_inst_bits +let enable_zcb () = !config_enable_zcb let enable_zfinx () = false let enable_writable_fiom () = !config_enable_writable_fiom let pmp_count () = !config_pmp_count diff --git a/ocaml_emulator/riscv_ocaml_sim.ml b/ocaml_emulator/riscv_ocaml_sim.ml index 03887b775..8dad8a45a 100644 --- a/ocaml_emulator/riscv_ocaml_sim.ml +++ b/ocaml_emulator/riscv_ocaml_sim.ml @@ -53,6 +53,9 @@ let options = Arg.align ([("-dump-dts", ("-mtval-has-illegal-inst-bits", Arg.Set P.config_mtval_has_illegal_inst_bits, " mtval stores instruction bits on an illegal instruction exception"); + ("-enable-zcb", + Arg.Set P.config_enable_zcb, + " enable Zcb (simple code size) extension"); ("-enable-writable-fiom", Arg.Set P.config_enable_writable_fiom, " enable FIOM (Fence of I/O implies Memory) bit in menvcfg");