From 5c9f3105c0ba7e57cc9d6d03319a3527e0af4594 Mon Sep 17 00:00:00 2001 From: Cole Nelson <35197931+cole-nelson@users.noreply.github.com> Date: Thu, 8 Oct 2020 20:33:40 -0400 Subject: [PATCH 01/47] Sparce isa integration (#1) Pulling SPARCE into master as basis for further development. --- run_tests.py | 71 ++- scripts/config_core.py | 4 +- source_code/caches/separate_caches.sv | 2 +- source_code/include/ahb_if.vh | 4 +- source_code/include/sparce_internal_if.vh | 87 ++++ source_code/include/sparce_pipeline_if.vh | 69 +++ source_code/pipelines/pipeline_wrapper.sv | 4 +- source_code/pipelines/tspp/tspp.sv | 4 +- .../pipelines/tspp/tspp_execute_stage.sv | 14 +- .../pipelines/tspp/tspp_fetch_stage.sv | 18 +- .../pipelines/tspp/tspp_hazard_unit.sv | 8 +- .../sparce/sparce_disabled/sparce_disabled.sv | 40 ++ .../sparce/sparce_enabled/sparce_cfid.sv | 46 ++ .../sparce/sparce_enabled/sparce_enabled.sv | 58 +++ .../sparce/sparce_enabled/sparce_psru.sv | 52 ++ .../sparce_enabled/sparce_sasa_table.sv | 209 ++++++++ .../sparce/sparce_enabled/sparce_sprf.sv | 73 +++ .../sparce/sparce_enabled/sparce_svc.sv | 34 ++ source_code/sparce/sparce_wrapper.sv | 43 ++ source_code/sparce/wscript | 8 + source_code/standard_core/RISCVBusiness.sv | 67 ++- source_code/standard_core/control_unit.sv | 2 +- source_code/standard_core/dmem_extender.sv | 48 ++ source_code/tb/tb_RISCVBusiness.sv | 2 +- source_code/tb/tb_RISCVBusiness_self_test.sv | 5 +- source_code/tb/tb_sparce_cfid.sv | 74 +++ source_code/tb/tb_sparce_psru.sv | 152 ++++++ source_code/tb/tb_sparce_sasa_table.sv | 484 ++++++++++++++++++ source_code/tb/tb_sparce_sprf.sv | 188 +++++++ source_code/tb/tb_sparce_svc.sv | 75 +++ source_code/wscript | 43 +- sparce.yml | 26 + verification/asm-env/selfasm/riscv_test.h | 12 +- verification/asm-env/selfasm/riscv_test.h.old | 184 ------- verification/asm-env/selfasm/test_macros.h | 23 + verification/asm-tests/RV32I/mult.S | 63 +++ verification/asm-tests/RV32I/sparce.S | 59 +++ .../RV32I/sparce_ctrlflow_nottaken.S | 67 +++ .../self-tests/RV32I/sparce_ctrlflow_taken.S | 88 ++++ .../RV32I/sparce_inflight_execute.S | 83 +++ .../self-tests/RV32I/sparce_inflight_fetch.S | 85 +++ .../self-tests/RV32I/sparce_sasa_disable.S | 88 ++++ .../self-tests/RV32I/sparce_single_skip.S | 50 ++ .../self-tests/RV32I/sparce_skip_range.S | 138 +++++ .../RV32I/sparce_sprf_uninitialized.S | 57 +++ 45 files changed, 2789 insertions(+), 222 deletions(-) create mode 100644 source_code/include/sparce_internal_if.vh create mode 100644 source_code/include/sparce_pipeline_if.vh create mode 100644 source_code/sparce/sparce_disabled/sparce_disabled.sv create mode 100644 source_code/sparce/sparce_enabled/sparce_cfid.sv create mode 100644 source_code/sparce/sparce_enabled/sparce_enabled.sv create mode 100644 source_code/sparce/sparce_enabled/sparce_psru.sv create mode 100644 source_code/sparce/sparce_enabled/sparce_sasa_table.sv create mode 100644 source_code/sparce/sparce_enabled/sparce_sprf.sv create mode 100644 source_code/sparce/sparce_enabled/sparce_svc.sv create mode 100644 source_code/sparce/sparce_wrapper.sv create mode 100644 source_code/sparce/wscript create mode 100644 source_code/tb/tb_sparce_cfid.sv create mode 100644 source_code/tb/tb_sparce_psru.sv create mode 100644 source_code/tb/tb_sparce_sasa_table.sv create mode 100644 source_code/tb/tb_sparce_sprf.sv create mode 100644 source_code/tb/tb_sparce_svc.sv create mode 100644 sparce.yml delete mode 100644 verification/asm-env/selfasm/riscv_test.h.old create mode 100755 verification/asm-tests/RV32I/mult.S create mode 100755 verification/asm-tests/RV32I/sparce.S create mode 100644 verification/self-tests/RV32I/sparce_ctrlflow_nottaken.S create mode 100644 verification/self-tests/RV32I/sparce_ctrlflow_taken.S create mode 100644 verification/self-tests/RV32I/sparce_inflight_execute.S create mode 100644 verification/self-tests/RV32I/sparce_inflight_fetch.S create mode 100644 verification/self-tests/RV32I/sparce_sasa_disable.S create mode 100644 verification/self-tests/RV32I/sparce_single_skip.S create mode 100644 verification/self-tests/RV32I/sparce_skip_range.S create mode 100644 verification/self-tests/RV32I/sparce_sprf_uninitialized.S diff --git a/run_tests.py b/run_tests.py index 713cfac97..e152209dc 100755 --- a/run_tests.py +++ b/run_tests.py @@ -38,7 +38,8 @@ FILE_NAME = None ARCH = "RV32I" SUPPORTED_ARCHS = [] -SUPPORTED_TEST_TYPES = ['asm', 'c', 'selfasm', ""] +SUPPORTED_TEST_TYPES = ['asm', 'c', 'selfasm', "sparce", ""] +SPARCE_MODULES = ['sparce_svc', 'sparce_sprf', 'sparce_sasa_table', 'sparce_psru', 'sparce_cfid'] TEST_TYPE = "" # Change this variable to the filename (minus extension) # of the top level file for your project. This should @@ -74,16 +75,19 @@ def parse_arguments(): SUPPORTED_ARCHS = glob.glob('./verification/' + test_file_dir + '*') SUPPORTED_ARCHS = [a.split('/'+test_file_dir)[1] for a in SUPPORTED_ARCHS] if ARCH not in SUPPORTED_ARCHS: - print "ERROR: No " + test_type + " tests exist for " + ARCH - sys.exit(1) - else: - if TEST_TYPE == 'selfasm': - test_file_dir = 'self-tests/' + if test_type != 'sparce': + print "ERROR: No " + test_type + " tests exist for " + ARCH + sys.exit(1) else: + if TEST_TYPE == 'sparce': + pass + elif TEST_TYPE == 'selfasm': + test_file_dir = 'self-tests/' + else: test_file_dir = TEST_TYPE + '-tests/' - SUPPORTED_ARCHS = glob.glob('./verification/' + test_file_dir + '*') - SUPPORTED_ARCHS = [a.split('/'+test_file_dir)[1] for a in SUPPORTED_ARCHS] - if ARCH not in SUPPORTED_ARCHS: + SUPPORTED_ARCHS = glob.glob('./verification/' + test_file_dir + '*') + SUPPORTED_ARCHS = [a.split('/'+test_file_dir)[1] for a in SUPPORTED_ARCHS] + if ARCH not in SUPPORTED_ARCHS: print "ERROR: No " + TEST_TYPE + " tests exist for " + ARCH sys.exit(1) @@ -550,6 +554,43 @@ def run_asm(): return failures +def run_sparce(): + failures = 0 + print "starting sparce module tests..." + for module in SPARCE_MODULES: + + pass_msg = '{0:<40}{1:>20}'.format(module,START_GREEN + '[PASSED]' + END_COLOR) + fail_msg = '{0:<40}{1:>20}'.format(module,START_RED + '[FAILED]' + END_COLOR) + + output_dir = './sim_out/sparce/' + module + '/' + if not os.path.exists(output_dir): + try: + os.makedirs(output_dir) + except OSError as exc: # Guard against race condition + if exc.errno != errno.EEXIST: + raise + cmd_arr = ['waf', 'configure', '--top_level=' + module] + failure = subprocess.call(cmd_arr, stdout=FNULL) + if failure: + print "Error configuring test for " + module + failures += 1 + else: + cmd_arr = ['waf', 'verify_source'] + log = open(output_dir + 'waf_output.log', 'w') + log.write('Now running ' + module) + failure = subprocess.call(cmd_arr, stdout=log) + if failure: + log.close() + log = open(output_dir + 'waf_output.log', 'r') + for line in log: + print line + failures += 1 + print fail_msg + else: + print pass_msg + + return failures + def run_selfasm(): failures = 0 if FILE_NAME is None: @@ -681,11 +722,15 @@ def run_c(): failures = run_c() # self tests elif TEST_TYPE == "selfasm": - failures = run_selfasm() + failures = run_selfasm() + # sparce tests + elif TEST_TYPE == "sparce": + failures = run_sparce() elif TEST_TYPE == "": - failures += run_asm() - failures += run_selfasm() - failures += run_c() + failures += run_asm() + failures += run_selfasm() + failures += run_c() + failures += run_sparce() else: print "To be implemented" sys.exit(failures) diff --git a/scripts/config_core.py b/scripts/config_core.py index 2a2e3edda..d85e42a96 100755 --- a/scripts/config_core.py +++ b/scripts/config_core.py @@ -44,7 +44,9 @@ 'icache_type' : ['pass_through', 'direct_mapped_tpf'], # Bus Configurations 'bus_endianness' : ['big', 'little'], - 'bus_interface_type' : ['ahb_if', 'generic_bus_if'] + 'bus_interface_type' : ['ahb_if', 'generic_bus_if'], + # Sparisty Optimizations + 'sparce_enabled' : [ 'enabled', 'disabled' ] } RISC_MGMT_PARAMS = \ diff --git a/source_code/caches/separate_caches.sv b/source_code/caches/separate_caches.sv index 3b47953e9..9a7a87448 100644 --- a/source_code/caches/separate_caches.sv +++ b/source_code/caches/separate_caches.sv @@ -32,7 +32,7 @@ module separate_caches ( generic_bus_if.cpu dcache_mem_gen_bus_if, generic_bus_if.generic_bus icache_proc_gen_bus_if, generic_bus_if.generic_bus dcache_proc_gen_bus_if, - cache_control_if cc_if + cache_control_if.caches cc_if ); generate case (DCACHE_TYPE) diff --git a/source_code/include/ahb_if.vh b/source_code/include/ahb_if.vh index 4bd188a6d..f16b176f0 100644 --- a/source_code/include/ahb_if.vh +++ b/source_code/include/ahb_if.vh @@ -8,7 +8,9 @@ `define AHB_IF_VH interface ahb_if; - logic [1:0] HTRANS, HRESP; + logic [1:0] HTRANS; + logic [1:0] HRESP; + //logic [1:0] HRESP; JOHN CHANGED THIS logic [2:0] HSIZE; logic [31:0] HADDR; logic [31:0] HWDATA; diff --git a/source_code/include/sparce_internal_if.vh b/source_code/include/sparce_internal_if.vh new file mode 100644 index 000000000..be172ba41 --- /dev/null +++ b/source_code/include/sparce_internal_if.vh @@ -0,0 +1,87 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_internal_if.vh +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/30/2019 +* Description: Interface containing the modports for all the internal +* components of SparCE, including the SVC, SASA Table, +* PSRU, and SpRF +*/ + +`ifndef SPARCE_INTERNAL_IF_VH +`define SPARCE_INTERNAL_IF_VH + + +typedef enum logic { + SASA_COND_OR = 1'b0, + SASA_COND_AND = 1'b1 +} sasa_cond_t; + +interface sparce_internal_if; + + import rv32i_types_pkg::*; + + // SVC + word_t wb_data; + logic is_sparse; + + // SpRF + logic wb_en, rs1_sparsity, rs2_sparsity; + + // SASA Table + word_t pc, sasa_addr, sasa_data, preceding_pc; + logic sasa_wen, valid, sasa_enable; + logic [4:0] sasa_rs1, sasa_rs2, rd; + sasa_cond_t condition; + logic [4:0] insts_to_skip; + + // PSRU + word_t sparce_target; + logic skipping; + + // CFID + logic ctrl_flow_enable; + word_t rdata; + + modport svc ( + output is_sparse, + input wb_data + ); + + modport sprf ( + output rs1_sparsity, rs2_sparsity, + input wb_en, rd, is_sparse, sasa_rs1, sasa_rs2 + ); + + modport sasa_table ( + output sasa_rs1, sasa_rs2, insts_to_skip, preceding_pc, condition, valid, + input pc, sasa_addr, sasa_data, sasa_wen, sasa_enable + ); + + modport psru ( + output skipping, sparce_target, + input valid, insts_to_skip, preceding_pc, condition, rs1_sparsity, rs2_sparsity, ctrl_flow_enable + ); + + modport cfid ( + output ctrl_flow_enable, + input rdata + ); +endinterface +`endif //SPARCE_PIPELINE_IF diff --git a/source_code/include/sparce_pipeline_if.vh b/source_code/include/sparce_pipeline_if.vh new file mode 100644 index 000000000..5b2c83c58 --- /dev/null +++ b/source_code/include/sparce_pipeline_if.vh @@ -0,0 +1,69 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_pipeline_if.vh +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/17/2019 +* Description: Interface between the sparsity optimization unit and the +* rest of the pipeline. Does not include internal signals +* between blocks needed for sparsity optimizations. +*/ + +`ifndef SPARCE_PIPELINE_IF_VH +`define SPARCE_PIPELINE_IF_VH + +interface sparce_pipeline_if; + + import rv32i_types_pkg::*; + + word_t pc, wb_data, sasa_data, sasa_addr; + logic wb_en, sasa_wen; + logic [4:0] rd; + logic if_ex_enable; + + word_t sparce_target, rdata; + logic skipping; + + modport pipeline ( + input sparce_target, skipping, + output pc, wb_data, wb_en, sasa_data, sasa_addr, sasa_wen, rd, if_ex_enable, rdata + ); + + modport pipe_fetch ( + input sparce_target, skipping, + output pc, rdata + ); + + modport pipe_execute ( + input sparce_target, skipping, + output wb_data, wb_en, sasa_data, sasa_addr, sasa_wen, rd + ); + + modport hazard ( + input sparce_target, skipping, + output if_ex_enable + ); + + modport sparce ( + output sparce_target, skipping, + input pc, wb_data, wb_en, sasa_data, sasa_addr, sasa_wen, rd, if_ex_enable, rdata + ); + + +endinterface +`endif //SPARCE_PIPELINE_IF diff --git a/source_code/pipelines/pipeline_wrapper.sv b/source_code/pipelines/pipeline_wrapper.sv index 64e971fc5..a9c9e22dd 100644 --- a/source_code/pipelines/pipeline_wrapper.sv +++ b/source_code/pipelines/pipeline_wrapper.sv @@ -28,6 +28,7 @@ `include "risc_mgmt_if.vh" `include "cache_control_if.vh" `include "component_selection_defines.vh" +`include "sparce_pipeline_if.vh" module pipeline_wrapper ( input logic CLK, nRST, @@ -37,7 +38,8 @@ module pipeline_wrapper ( prv_pipeline_if prv_pipe_if, predictor_pipeline_if predict_if, risc_mgmt_if rm_if, - cache_control_if cc_if + cache_control_if cc_if, + sparce_pipeline_if sparce_if ); tspp tspp_pipeline(.*); diff --git a/source_code/pipelines/tspp/tspp.sv b/source_code/pipelines/tspp/tspp.sv index 4af0ea493..f6ba969c7 100644 --- a/source_code/pipelines/tspp/tspp.sv +++ b/source_code/pipelines/tspp/tspp.sv @@ -29,6 +29,7 @@ `include "prv_pipeline_if.vh" `include "risc_mgmt_if.vh" `include "cache_control_if.vh" +`include "sparce_pipeline_if.vh" module tspp ( input logic CLK, nRST, @@ -38,7 +39,8 @@ module tspp ( prv_pipeline_if prv_pipe_if, predictor_pipeline_if predict_if, risc_mgmt_if rm_if, - cache_control_if cc_if + cache_control_if cc_if, + sparce_pipeline_if sparce_if ); //interface instantiations tspp_fetch_execute_if fetch_ex_if(); diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index b23357168..540cc855c 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -34,6 +34,7 @@ `include "risc_mgmt_if.vh" `include "cache_control_if.vh" + module tspp_execute_stage( input logic CLK, nRST, tspp_fetch_execute_if.execute fetch_ex_if, @@ -43,7 +44,8 @@ module tspp_execute_stage( prv_pipeline_if.pipe prv_pipe_if, output logic halt, risc_mgmt_if.ts_execute rm_if, - cache_control_if.pipeline cc_if + cache_control_if.pipeline cc_if, + sparce_pipeline_if.pipe_execute sparce_if ); import rv32i_types_pkg::*; @@ -386,6 +388,16 @@ module tspp_execute_stage( assign predict_if.prediction = fetch_ex_if.fetch_ex_reg.prediction; assign predict_if.branch_result = branch_if.branch_taken; //predict_if.update_addr = ; + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + assign sparce_if.wb_data = rf_if.w_data; + assign sparce_if.wb_en = rf_if.wen; + assign sparce_if.sasa_data = rf_if.rs2_data; + assign sparce_if.sasa_addr = alu_if.port_out; + assign sparce_if.sasa_wen = cu_if.dwen; + assign sparce_if.rd = rf_if.rd; /********************************************************* *** Signals for Bind Tracking - Read-Only, These don't affect execution diff --git a/source_code/pipelines/tspp/tspp_fetch_stage.sv b/source_code/pipelines/tspp/tspp_fetch_stage.sv index 905aa0726..37a30dbf6 100644 --- a/source_code/pipelines/tspp/tspp_fetch_stage.sv +++ b/source_code/pipelines/tspp/tspp_fetch_stage.sv @@ -27,18 +27,20 @@ `include "predictor_pipeline_if.vh" `include "generic_bus_if.vh" `include "component_selection_defines.vh" +`include "cache_control_if.vh" module tspp_fetch_stage ( input logic CLK, nRST, tspp_fetch_execute_if.fetch fetch_ex_if, tspp_hazard_unit_if.fetch hazard_if, predictor_pipeline_if.access predict_if, - generic_bus_if.cpu igen_bus_if + generic_bus_if.cpu igen_bus_if, + sparce_pipeline_if.pipe_fetch sparce_if ); import rv32i_types_pkg::*; - //parameter RESET_PC = 32'h200; - parameter RESET_PC = 32'h80000000; + parameter RESET_PC = 32'h200; + //parameter RESET_PC = 32'h80000000; word_t pc, pc4, npc, instr; @@ -54,8 +56,8 @@ module tspp_fetch_stage ( assign pc4 = pc + 4; assign predict_if.current_pc = pc; - assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc : (hazard_if.npc_sel ? fetch_ex_if.brj_addr : - (predict_if.predict_taken ? predict_if.target_addr : pc4)); + assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc : ( sparce_if.skipping ? sparce_if.sparce_target : (hazard_if.npc_sel ? fetch_ex_if.brj_addr : + (predict_if.predict_taken ? predict_if.target_addr : pc4))); //Instruction Access logic assign hazard_if.i_mem_busy = igen_bus_if.busy; @@ -96,6 +98,12 @@ module tspp_fetch_stage ( endian_swapper ltb_endian(igen_bus_if.rdata, instr); endgenerate + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + + assign sparce_if.pc = pc; + assign sparce_if.rdata = igen_bus_if.rdata; endmodule diff --git a/source_code/pipelines/tspp/tspp_hazard_unit.sv b/source_code/pipelines/tspp/tspp_hazard_unit.sv index f84a04024..163841f04 100644 --- a/source_code/pipelines/tspp/tspp_hazard_unit.sv +++ b/source_code/pipelines/tspp/tspp_hazard_unit.sv @@ -31,7 +31,8 @@ module tspp_hazard_unit ( tspp_hazard_unit_if.hazard_unit hazard_if, prv_pipeline_if.hazard prv_pipe_if, - risc_mgmt_if.ts_hazard rm_if + risc_mgmt_if.ts_hazard rm_if, + sparce_pipeline_if.hazard sparce_if ); import alu_types_pkg::*; import rv32i_types_pkg::*; @@ -111,4 +112,9 @@ module tspp_hazard_unit assign prv_pipe_if.badaddr = (hazard_if.mal_insn | hazard_if.fault_insn) ? hazard_if.badaddr_f : (rm_if.exception ? rm_if.mem_addr : hazard_if.badaddr_e); + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + assign sparce_if.if_ex_enable = rm_if.if_ex_enable; + endmodule diff --git a/source_code/sparce/sparce_disabled/sparce_disabled.sv b/source_code/sparce/sparce_disabled/sparce_disabled.sv new file mode 100644 index 000000000..1a6127f93 --- /dev/null +++ b/source_code/sparce/sparce_disabled/sparce_disabled.sv @@ -0,0 +1,40 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_disabled.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/29/2019 +* Description: The top level file for a CPU without sparsity optimizations +* enabled +*/ + +`include "sparce_pipeline_if.vh" + +module sparce_disabled( + input logic CLK, nRST, + sparce_pipeline_if.sparce sparce_if +); + + // when disabled, sparce should never force the pipeline to skip + + // all inputs are to be ignored + assign sparce_if.skipping = 1'b0; + assign sparce_if.sparce_target = '0; + + +endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_cfid.sv b/source_code/sparce/sparce_enabled/sparce_cfid.sv new file mode 100644 index 000000000..8c2fffe0a --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_cfid.sv @@ -0,0 +1,46 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_cfid.sv +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: Oct 1st, 2019 +* Description: The file containing the control flow instruction detector. +*/ + +// modport cfid ( +// output ctrl_flow_enable, +// input rdata +// ); + +`include "sparce_internal_if.vh" + +module sparce_cfid(sparce_internal_if.cfid cfid_if); + import rv32i_types_pkg::*; + + opcode_t cf_op; + assign cf_op = opcode_t'(cfid_if.rdata[OP_W-1:0]); + + always_comb begin + if (cf_op == BRANCH || cf_op == JAL || cf_op == JALR) + cfid_if.ctrl_flow_enable = 0; + else + cfid_if.ctrl_flow_enable = 1; + end + +endmodule + diff --git a/source_code/sparce/sparce_enabled/sparce_enabled.sv b/source_code/sparce/sparce_enabled/sparce_enabled.sv new file mode 100644 index 000000000..b80321d5d --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_enabled.sv @@ -0,0 +1,58 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_enabled.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/29/2019 +* Description: The top level file for sparsity enabled for the CPU +*/ + +`include "sparce_pipeline_if.vh" +`include "sparce_internal_if.vh" + +module sparce_enabled( + input logic CLK, nRST, + sparce_pipeline_if.sparce sparce_if +); + + sparce_internal_if internal_if(); + + // assign inputs to the internal modules + assign internal_if.pc = sparce_if.pc; + assign internal_if.wb_data = sparce_if.wb_data; + assign internal_if.wb_en = sparce_if.wb_en; + assign internal_if.sasa_data = sparce_if.sasa_data; + assign internal_if.sasa_addr = sparce_if.sasa_addr; + assign internal_if.sasa_wen = sparce_if.sasa_wen; + assign internal_if.rd = sparce_if.rd; + assign internal_if.sasa_enable = sparce_if.if_ex_enable; + assign internal_if.rdata = sparce_if.rdata; + + // assign to sparce module outputs + assign sparce_if.skipping = internal_if.skipping; + assign sparce_if.sparce_target = internal_if.sparce_target; + + // instantiate internal modules + sparce_svc sparce_svc_i(internal_if.svc); + sparce_sprf sparce_sprf_i(CLK, nRST, internal_if.sprf); + sparce_sasa_table #(.SASA_SETS(4)) sparce_sasa_table_i (CLK, nRST, internal_if.sasa_table); + sparce_psru sparce_psru_i(internal_if.psru); + sparce_cfid sparce_cfid_i(internal_if.cfid); + + +endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_psru.sv b/source_code/sparce/sparce_enabled/sparce_psru.sv new file mode 100644 index 000000000..c1b778fba --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_psru.sv @@ -0,0 +1,52 @@ + +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_psru.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/14/2019 +* Description: The file containing pre-identify and skip redundancy unit +*/ + +`include "sparce_internal_if.vh" + +// modport psru ( +// output skipping, sparce_target, +// input valid, insts_to_skip, preceding_pc, condition, rs1_sparsity, rs2_sparsity, ctrl_flow_enable +// ); + +module sparce_psru(sparce_internal_if.psru psru_if); + + always_comb begin + if (psru_if.valid) begin + // choose the correct condition to evaluate + if(psru_if.condition == SASA_COND_OR) begin + psru_if.skipping = (psru_if.rs1_sparsity || psru_if.rs2_sparsity) && psru_if.ctrl_flow_enable; + end else begin + psru_if.skipping = (psru_if.rs1_sparsity && psru_if.rs2_sparsity) && psru_if.ctrl_flow_enable; + end + // calculate the new program counter + psru_if.sparce_target = psru_if.preceding_pc + (psru_if.insts_to_skip << 2) + 4; + end else begin + // don't skip if the SASA table entry is invalid + psru_if.skipping = 1'b0; + psru_if.sparce_target = '1; + end + end + +endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_sasa_table.sv b/source_code/sparce/sparce_enabled/sparce_sasa_table.sv new file mode 100644 index 000000000..ad6d2a82f --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_sasa_table.sv @@ -0,0 +1,209 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_sasa_table.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/6/2019 +* Description: The file containing the SASA table +*/ + +`include "sparce_internal_if.vh" + +// defined because $clog2 is not universally supported +`define CLOG2(x) \ + (((x) <= 1) ? 0 : \ + ((x) <= 2) ? 1 : \ + ((x) <= 4) ? 2 : \ + ((x) <= 8) ? 3 : \ + ((x) <= 16) ? 4 : \ + ((x) <= 32) ? 5 : \ + ((x) <= 64) ? 6 : \ + -1) + +// modport sasa_table ( +// output sasa_rs1, sasa_rs2, insts_to_skip, preceding_pc, condition, valid, +// input pc, sasa_addr, sasa_data, sasa_wen +// ); + + +// struct for the data read from memory +typedef struct packed { + logic [15:0] prev_pc; + logic [4:0] rs1; + logic [4:0] rs2; + sasa_cond_t sasa_cond; + logic [4:0] insts_to_skip; +} sasa_input_t; + + +module sparce_sasa_table #(parameter SASA_ENTRIES = 16, parameter SASA_SETS = 1, parameter SASA_ADDR = 32'h90000000) (input logic CLK, nRST, sparce_internal_if.sasa_table sasa_if); + + // struct for each entry in the SASA table + typedef struct packed { + logic [1:0] usage; + logic valid; + logic [15:0] tag; + logic [4:0] rs1; + logic [4:0] rs2; + sasa_cond_t sasa_cond; + logic [4:0] insts_to_skip; + } sasa_entry_t; + + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][`CLOG2(SASA_SETS) - 1 :0] usage; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0] valid; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][15- `CLOG2(SASA_SETS) :0] tag; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][4:0] rs1; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][4:0] rs2; + sasa_cond_t [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0] sasa_cond; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][4:0] insts_to_skip; + sasa_input_t input_data; + + logic [`CLOG2(SASA_ENTRIES/SASA_SETS)-1:0] input_idx; + logic [`CLOG2(SASA_ENTRIES/SASA_SETS)-1:0] pc_idx; + logic [15-`CLOG2(SASA_ENTRIES/SASA_SETS):0] pc_tag; + logic sasa_match; + logic existing_entry; + logic [`CLOG2(SASA_SETS)-1:0] existing_entry_set; + + logic [SASA_SETS:0] sasa_hits; + logic sasa_config, sasa_config_match; + + // sasa table configuration register + assign sasa_config_match = sasa_if.sasa_enable && sasa_if.sasa_wen && (sasa_if.sasa_addr == SASA_ADDR + 4); + + always_ff @(posedge CLK, negedge nRST) begin : sasa_configuration + if (!nRST) begin + sasa_config <= '0; + end + else begin + sasa_config <= sasa_config; + if (sasa_config_match) begin + sasa_config <= sasa_if.sasa_data; + end + end + end + + // wiring for indexing of the cache arrays + assign input_idx = (SASA_ENTRIES == SASA_SETS) ? 0 : input_data.prev_pc; + assign pc_idx = (SASA_ENTRIES == SASA_SETS) ? 0 : (sasa_if.pc >> 2); // ignore byte addressed aspect + assign pc_tag = sasa_if.pc >> (`CLOG2(SASA_ENTRIES/SASA_SETS) + 2); + assign sasa_match = sasa_if.sasa_enable && sasa_if.sasa_wen && (sasa_if.sasa_addr == SASA_ADDR); + + + always_comb begin : sasa_input_conversion + input_data.prev_pc = sasa_if.sasa_data[31:16]; + input_data.rs1 = sasa_if.sasa_data[15:11]; + input_data.rs2 = sasa_if.sasa_data[10:6]; + input_data.sasa_cond = sasa_cond_t'(sasa_if.sasa_data[5]); + input_data.insts_to_skip = sasa_if.sasa_data[4:0]; + end + + always_ff @(posedge CLK, negedge nRST) begin : sasa_table_entries + if (!nRST) begin + // for loops just to set different usage values per entry + for(int i = 0; i < SASA_SETS; i++) begin + for(int j = 0; j < SASA_ENTRIES/SASA_SETS; j++) begin + // set default usage values to 0, 1, 2, 3 for LRU + usage[i][j]<= (SASA_SETS-1)-i; + valid[i][j]<= 1'b0; + tag[i][j]<= '0; + rs1[i][j]<= '0; + rs2[i][j]<= '0; + sasa_cond[i][j]<= SASA_COND_OR; + insts_to_skip[i][j]<= '0; + end + end + end else begin + usage <= usage; + valid <= valid; + tag <= tag; + rs1 <= rs1; + rs2 <= rs2; + sasa_cond <= sasa_cond; + insts_to_skip <= insts_to_skip; + // If the software is attempting to write to the SASA table, write in + // the data and then update the LRU usage + if(sasa_match) begin + if(!existing_entry) begin + for (int i = 0; i < SASA_SETS; i++) begin + if (usage[i][input_idx]== '1 || SASA_SETS == 1) begin + valid[i][input_idx]<= 1; + tag[i][input_idx]<= input_data.prev_pc >> (`CLOG2(SASA_ENTRIES/SASA_SETS)); + rs1[i][input_idx]<= input_data.rs1; + rs2[i][input_idx]<= input_data.rs2; + sasa_cond[i][input_idx]<= input_data.sasa_cond; + insts_to_skip[i][input_idx]<= input_data.insts_to_skip; + end + usage[i][input_idx]<= (usage[i][input_idx]+ 1) % SASA_SETS; + end + end else begin + valid[existing_entry_set][input_idx]<= 1; + tag[existing_entry_set][input_idx]<= input_data.prev_pc >> (`CLOG2(SASA_ENTRIES/SASA_SETS)); + rs1[existing_entry_set][input_idx]<= input_data.rs1; + rs2[existing_entry_set][input_idx]<= input_data.rs2; + sasa_cond[existing_entry_set][input_idx]<= input_data.sasa_cond; + insts_to_skip[existing_entry_set][input_idx]<= input_data.insts_to_skip; + for (int i = 0; i < SASA_SETS; i++) begin + if (usage[i][input_idx] < usage[existing_entry_set][input_idx]) begin + usage[i][input_idx]<= (usage[i][input_idx]+ 1) % SASA_SETS; + end + end + end + end + // If the PC matches in the SASA table, update the LRU usage + else if (sasa_hits != 0) begin + for (int i = 0; i < SASA_SETS; i++) begin + if(usage[i][pc_idx] < usage[sasa_hits-1][pc_idx]) begin + usage[i][pc_idx]<= (usage[i][pc_idx]+ 1) % SASA_SETS; + end else if (i == sasa_hits - 1) begin + usage[i][pc_idx] <= '0; + end + end + end + end + end + + always_comb begin : sasa_outputs + sasa_if.sasa_rs1 = '0; + sasa_if.sasa_rs2 = '0; + sasa_if.insts_to_skip = '0; + sasa_if.preceding_pc = sasa_if.pc; + sasa_if.condition = SASA_COND_OR; + sasa_if.valid = 1'b0; + sasa_hits = '0; + existing_entry = 0; + for (int i = 0; i < SASA_SETS; i++) begin + if (valid[i][pc_idx]&& (tag[i][pc_idx]== pc_tag)) begin + sasa_hits = i+1; + // ensure skipping is only valid for PC < 'hFFFF FFFC + //sasa_if.valid = (sasa_config == '0); + sasa_if.valid = (sasa_config == '0) && (sasa_if.pc[31:18] == '0); + sasa_if.sasa_rs1 = rs1[i][pc_idx]; + sasa_if.sasa_rs2 = rs2[i][pc_idx]; + sasa_if.condition = sasa_cond[i][pc_idx]; + sasa_if.insts_to_skip = insts_to_skip[i][pc_idx]; + end + if (valid[i][input_idx]&& (tag[i][input_idx] == input_data.prev_pc >> (`CLOG2(SASA_ENTRIES/SASA_SETS)))) begin + existing_entry = 1; + existing_entry_set = i; + end + end + end + +endmodule + diff --git a/source_code/sparce/sparce_enabled/sparce_sprf.sv b/source_code/sparce/sparce_enabled/sparce_sprf.sv new file mode 100644 index 000000000..5c3696623 --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_sprf.sv @@ -0,0 +1,73 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_sprf.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/4/2019 +* Description: The file containing the sparsity register file (SpRF) +* module +*/ + +`include "sparce_internal_if.vh" + +module sparce_sprf(input logic CLK, nRST, sparce_internal_if.sprf sprf_if); + + logic [31:1] sparsity_reg; + logic [31:0] sparsity_out; + + // all register outputs are tied to the registers, except for + // register[0] which is always 1 + assign sparsity_out[0] = 1'b1; + assign sparsity_out[31:1] = sparsity_reg[31:1]; + + // register update logic + always_ff @(posedge CLK, negedge nRST) begin + if(!nRST) begin + // even through regs are initialized to 0, don't treat + // uninitialized registers as sparse registers + sparsity_reg[31:1] <= '0; + end else begin + // keep registers as old values when not modified + sparsity_reg[31:1] <= sparsity_reg[31:1]; + // modify the chosen register if the pipeline is writing back + if(sprf_if.wb_en) begin + sparsity_reg[sprf_if.rd] <= sprf_if.is_sparse; + end + end + end + + // register output logic + always_comb begin + if (sprf_if.sasa_rs1 == '0) begin + sprf_if.rs1_sparsity = 1'b1; + end else if (sprf_if.sasa_rs1 == sprf_if.rd && sprf_if.wb_en) begin + sprf_if.rs1_sparsity = sprf_if.is_sparse; + end else begin + sprf_if.rs1_sparsity = sparsity_out[sprf_if.sasa_rs1]; + end + if (sprf_if.sasa_rs2 == '0) begin + sprf_if.rs2_sparsity = 1'b1; + end else if (sprf_if.sasa_rs2 == sprf_if.rd && sprf_if.wb_en) begin + sprf_if.rs2_sparsity = sprf_if.is_sparse; + end else begin + sprf_if.rs2_sparsity = sparsity_out[sprf_if.sasa_rs2]; + end + end + +endmodule + diff --git a/source_code/sparce/sparce_enabled/sparce_svc.sv b/source_code/sparce/sparce_enabled/sparce_svc.sv new file mode 100644 index 000000000..50cf7fe44 --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_svc.sv @@ -0,0 +1,34 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_svc.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/4/2019 +* Description: The file containing the sparsity value chekcer (SVC) +* module +*/ + +`include "sparce_internal_if.vh" + +module sparce_svc(sparce_internal_if.svc svc_if); + + // sparsity only when the writeback data is zero + assign svc_if.is_sparse = (svc_if.wb_data == 0); + +endmodule + diff --git a/source_code/sparce/sparce_wrapper.sv b/source_code/sparce/sparce_wrapper.sv new file mode 100644 index 000000000..8d82f4159 --- /dev/null +++ b/source_code/sparce/sparce_wrapper.sv @@ -0,0 +1,43 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_wrapper.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/17/2019 +* Description: The top level wrapper file for implementations of +* sparisity optimiations based on the SparCE paper +*/ + +`include "sparce_pipeline_if.vh" +`include "component_selection_defines.vh" + +module sparce_wrapper( + input logic CLK, nRST, + sparce_pipeline_if.sparce sparce_if +); + + // Sparsity blocks used based on the SPARCE_ENABLED definition + generate + case (SPARCE_ENABLED) + "disabled" : sparce_disabled sparce(.*); + "enabled" : sparce_enabled sparce(.*); + endcase + endgenerate + +endmodule + diff --git a/source_code/sparce/wscript b/source_code/sparce/wscript new file mode 100644 index 000000000..8eb0fdef4 --- /dev/null +++ b/source_code/sparce/wscript @@ -0,0 +1,8 @@ +#! /usr/bin/env python +#encoding: utf-8 + +def configure(cnf): + cnf.recurse(tspp) + +def sim_source(cnf): + cnf.recurse(tspp) diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index 9a0844b55..817878f73 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -27,11 +27,12 @@ `include "component_selection_defines.vh" `include "risc_mgmt_if.vh" `include "cache_control_if.vh" +`include "sparce_pipeline_if.vh" +`include "tspp_fetch_execute_if.vh" +`include "tspp_hazard_unit_if.vh" module RISCVBusiness ( input logic CLK, nRST, - output logic halt, - `ifdef BUS_INTERFACE_GENERIC_BUS generic_bus_if.cpu gen_bus_if `elsif BUS_INTERFACE_AHB @@ -50,9 +51,16 @@ module RISCVBusiness ( predictor_pipeline_if predict_if(); prv_pipeline_if prv_pipe_if(); cache_control_if cc_if(); + sparce_pipeline_if sparce_if(); - // Module Instantiations + //interface instantiations + tspp_fetch_execute_if fetch_ex_if(); + tspp_hazard_unit_if hazard_if(); + + logic halt; //JOHN CHANGED THIS + // Module Instantiations +/* pipeline_wrapper pipeline ( .CLK(CLK), .nRST(nRST), @@ -62,9 +70,42 @@ module RISCVBusiness ( .prv_pipe_if(prv_pipe_if), .predict_if(predict_if), .rm_if(rm_if), - .cc_if(cc_if) + .cc_if(cc_if), + .sparce_if(sparce_if) + ); +*/ + tspp_fetch_stage fetch_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .igen_bus_if(tspp_icache_gen_bus_if), + .sparce_if(sparce_if) + ); + + tspp_execute_stage execute_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .dgen_bus_if(tspp_dcache_gen_bus_if), + .prv_pipe_if(prv_pipe_if), + .halt(halt), + .rm_if(rm_if), + .cc_if(cc_if), + .sparce_if(sparce_if) ); + tspp_hazard_unit hazard_unit_i ( + .hazard_if(hazard_if), + .prv_pipe_if(prv_pipe_if), + .rm_if(rm_if), + .sparce_if(sparce_if) + ); + + branch_predictor_wrapper branch_predictor_i ( .CLK(CLK), .nRST(nRST), @@ -83,6 +124,7 @@ module RISCVBusiness ( .rm_if(rm_if) ); +/* caches_wrapper caches ( .CLK(CLK), .nRST(nRST), @@ -92,6 +134,17 @@ module RISCVBusiness ( .dcache_mem_gen_bus_if(dcache_mc_if), .cc_if(cc_if) ); +*/ + + separate_caches sep_caches ( + .CLK(CLK), + .nRST(nRST), + .icache_proc_gen_bus_if(tspp_icache_gen_bus_if), + .icache_mem_gen_bus_if(icache_mc_if), + .dcache_proc_gen_bus_if(tspp_dcache_gen_bus_if), + .dcache_mem_gen_bus_if(dcache_mc_if), + .cc_if(cc_if) + ); memory_controller mc ( .CLK(CLK), @@ -101,6 +154,12 @@ module RISCVBusiness ( .out_gen_bus_if(pipeline_trans_if) ); + sparce_wrapper sparce_wrapper_i ( + .CLK(CLK), + .nRST(nRST), + .sparce_if(sparce_if) + ); + // Instantiate the chosen bus interface generate diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index 71e756830..db1b7152c 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -188,7 +188,7 @@ module control_unit // TODO: FIX ME WHEN IMPLEMENTING INTERRUPTS assign cu_if.halt = (cu_if.instr == 32'h0000006f); // Privilege Control Signals - assign cu_if.fault_insn = 'b0; + assign cu_if.fault_insn = '0; always_comb begin case(cu_if.opcode) diff --git a/source_code/standard_core/dmem_extender.sv b/source_code/standard_core/dmem_extender.sv index e3a951bce..dc84cc07a 100644 --- a/source_code/standard_core/dmem_extender.sv +++ b/source_code/standard_core/dmem_extender.sv @@ -31,6 +31,54 @@ module dmem_extender ( output word_t ext_out ); +/* + always_comb begin + casez (load_type) + LB : begin + casez (byte_en) + 4'b0001 : ext_out = 32'(signed'(dmem_in[7:0])); + 4'b0010 : ext_out = 32'(signed'(dmem_in[15:8])); + 4'b0100 : ext_out = 32'(signed'(dmem_in[23:16])); + 4'b1000 : ext_out = 32'(signed'(dmem_in[31:24])); + default : ext_out = '0; + endcase + end + + LBU : begin + casez (byte_en) + 4'b0001 : ext_out = 32'({'0,dmem_in[7:0]}); + 4'b0010 : ext_out = 32'({'0,dmem_in[15:8]}); + 4'b0100 : ext_out = 32'({'0,dmem_in[23:16]}); + 4'b1000 : ext_out = 32'({'0,dmem_in[31:24]}); + default : ext_out = '0; + endcase + end + + LH : begin + casez (byte_en) + 4'b0011 : ext_out = 32'(signed'(dmem_in[15:0])); + 4'b1100 : ext_out = 32'(signed'(dmem_in[31:16])); + default : ext_out = '0; + endcase + end + + LHU : begin + casez (byte_en) + 4'b0011 : ext_out = 32'({'0,dmem_in[15:0]}); + 4'b1100 : ext_out = 32'({'0,dmem_in[31:16]}); + default : ext_out = '0; + endcase + end + + LW : ext_out = dmem_in; + + default : ext_out = '0; + endcase + end +*/ + + + always_comb begin casez (load_type) diff --git a/source_code/tb/tb_RISCVBusiness.sv b/source_code/tb/tb_RISCVBusiness.sv index 5ddc0a990..c0388276b 100644 --- a/source_code/tb/tb_RISCVBusiness.sv +++ b/source_code/tb/tb_RISCVBusiness.sv @@ -55,7 +55,7 @@ module tb_RISCVBusiness (); RISCVBusiness DUT ( .CLK(CLK), .nRST(nRST), - .halt(halt), + //.halt(halt), .gen_bus_if(rvb_gen_bus_if) ); diff --git a/source_code/tb/tb_RISCVBusiness_self_test.sv b/source_code/tb/tb_RISCVBusiness_self_test.sv index ef38c5e69..fe983e616 100644 --- a/source_code/tb/tb_RISCVBusiness_self_test.sv +++ b/source_code/tb/tb_RISCVBusiness_self_test.sv @@ -56,7 +56,6 @@ module tb_RISCVBusiness_self_test (); RISCVBusiness DUT ( .CLK(CLK), .nRST(nRST), - .halt(halt), .gen_bus_if(rvb_gen_bus_if) ); @@ -159,9 +158,9 @@ module tb_RISCVBusiness_self_test (); // Check Register 28 to see if test passed or failed if (clk_count == `RVBSELF_CLK_TIMEOUT) $display("ERROR: Test timed out"); - else if(DUT.pipeline.tspp_pipeline.execute_stage_i.rf.registers[28] != 32'h1) + else if(DUT.execute_stage_i.rf.registers[28] != 32'h1) $display("ERROR: Test %0d did not pass", - (DUT.pipeline.tspp_pipeline.execute_stage_i.rf.registers[28] - 1)/2); + (DUT.execute_stage_i.rf.registers[28] - 1)/2); else $display("SUCCESS"); $finish; diff --git a/source_code/tb/tb_sparce_cfid.sv b/source_code/tb/tb_sparce_cfid.sv new file mode 100644 index 000000000..81d8b2a58 --- /dev/null +++ b/source_code/tb/tb_sparce_cfid.sv @@ -0,0 +1,74 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_cfid.sv +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: Oct 3rd, 2019 +* Description: Testbench for the control flow instruction detector +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + +typedef struct packed +{ + word_t rdata; + logic enable; +} sparce_cfid_testvec_t; + +module tb_sparce_cfid (); + + parameter PERIOD = 20; + + sparce_internal_if sparce_if(); + sparce_cfid DUT(sparce_if); + + logic tb_clk; + integer i; + sparce_cfid_testvec_t testvec[4:0]; + + assign testvec = + { + {{{25{1'b0}}, JAL}, 1'b0 }, + {{{25{1'b0}}, JALR}, 1'b0 }, + {{{25{1'b0}}, LOAD}, 1'b1 }, + {{{25{1'b0}}, BRANCH}, 1'b0 }, + {{{25{1'b0}}, IMMED}, 1'b1 } + }; + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + initial begin + tb_clk <= 0; + i = 0; + @(posedge tb_clk); + while (i<$size(testvec)) begin + @(negedge tb_clk); + sparce_if.rdata = testvec[i].rdata; + @(posedge tb_clk); + assert (sparce_if.ctrl_flow_enable == testvec[i].enable) + else + $error("INCORRECT ENABLE"); + i = i+1; + end + $finish; + end +endmodule diff --git a/source_code/tb/tb_sparce_psru.sv b/source_code/tb/tb_sparce_psru.sv new file mode 100644 index 000000000..84c1e3d00 --- /dev/null +++ b/source_code/tb/tb_sparce_psru.sv @@ -0,0 +1,152 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_psru.sv +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: 08/27/2019 +* Description: Testbench for the Pre-identify and Skip Redundancy Unit +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + + // modport psru ( + // output skipping, sparce_target, + // input valid, insts_to_skip, preceding_pc, condition, rs1_sparsity, rs2_sparsity, ctrl_flow_enable + // ); + + +module tb_sparce_psru (); + + parameter PERIOD = 20; + integer i; + logic tb_clk; + logic temp_skip; + + sparce_internal_if sparce_if(); + sparce_psru DUT(sparce_if); + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + initial begin + tb_clk = 0; + $display("TEST0: Verify Sparce skipping and target values when SASA is invalid"); + initialize; + test_sasa_invalid; + $display("TEST1: Verify Sparce skipping when SASA is valid"); + initialize; + test_psru_skipping; + $display("TEST2: Verify Sparce target when SASA is valid"); + test_psru_target; + $display("TEST3: Verify control flow enabling"); + initialize; + test_ctrl_flow_enable; + $finish; + end + + // initialize psru port values + task initialize; + i = 0; + sparce_if.valid = 1'b0; + sparce_if.insts_to_skip = 5'd4; + sparce_if.preceding_pc = 32'h3000; + sparce_if.condition = SASA_COND_OR; + sparce_if.rs1_sparsity= 1'b0; + sparce_if.rs2_sparsity= 1'b0; + sparce_if.ctrl_flow_enable = 1'b1; + endtask + + // Test0: Verify sparce skipping and target values when SASA is invalid + task test_sasa_invalid; + for (i = 0; i < 8; i++) begin + @(negedge tb_clk); + sparce_if.condition = sasa_cond_t'(i[2]); + sparce_if.rs1_sparsity = i[1]; + sparce_if.rs2_sparsity = i[0]; + @(posedge tb_clk); + assert (sparce_if.skipping == 1'b0) $display ("PASSED: PSRU not skipped when SASA invalid"); + else $error("FAILED: SASA invalid but psru skipped"); + end + endtask + + // Test1: Verify sparce skipping when skip conditions are correct + task test_psru_skipping; + sparce_if.valid = 1'b1; + sparce_if.condition = SASA_COND_OR; + for (i = 0; i < 4; i++) begin + @(negedge tb_clk); + sparce_if.rs1_sparsity = i[1]; + sparce_if.rs2_sparsity = i[0]; + @(posedge tb_clk); + assert (sparce_if.skipping == (i[0] | i[1])) $display ("PASSED: PSRU skipped correctly when SASA valid"); + else $error("FAILED: SASA valid but psru skipped incorrectly"); + end + + sparce_if.condition = SASA_COND_AND; + for (i = 0; i < 4; i++) begin + @(negedge tb_clk); + sparce_if.rs1_sparsity = i[1]; + sparce_if.rs2_sparsity = i[0]; + @(posedge tb_clk); + assert (sparce_if.skipping == (i[0] & i[1])) $display ("PASSED: PSRU skipped correctly when SASA valid"); + else $error("FAILED: SASA valid but psru skipped incorrectly"); + end + endtask + + // Test2: Verify sparce target calculated correctly + task test_psru_target; + sparce_if.valid = 1'b1; + sparce_if.condition = SASA_COND_OR; + sparce_if.rs1_sparsity = 1'b1; + sparce_if.rs2_sparsity = 1'b1; + for (i = 0; i <= 5'h1F; i++) begin + @(negedge tb_clk); + sparce_if.insts_to_skip = i; + @(posedge tb_clk); + assert (sparce_if.sparce_target == sparce_if.preceding_pc + (i << 2) + 4) + else $error("FAILED: Skip target incorrect"); + end + $display("Test2 finished. PASSED if no assertion errors"); + endtask + + // Test3: Verify skipping suppression/enable for control flow instructions + task test_ctrl_flow_enable; + sparce_if.valid = 1'b1; + for (i = 0; i < 8; i++) begin + @(negedge tb_clk); + sparce_if.condition = i[0]; + sparce_if.rs1_sparsity = i[1]; + sparce_if.rs2_sparsity = i[2]; + sparce_if.ctrl_flow_enable = i[3]; + @(posedge tb_clk); + if (sparce_if.condition == SASA_COND_OR) + temp_skip = sparce_if.rs1_sparsity || sparce_if.rs2_sparsity; + else + temp_skip = sparce_if.rs1_sparsity && sparce_if.rs2_sparsity; + + assert ((temp_skip && sparce_if.ctrl_flow_enable) == sparce_if.skipping) + else + $error("Test3 ENABLE for skipping incorrect"); + end + $display("Test3 finished. PASSED if no assertion errors"); + endtask + +endmodule diff --git a/source_code/tb/tb_sparce_sasa_table.sv b/source_code/tb/tb_sparce_sasa_table.sv new file mode 100644 index 000000000..cf522c9b8 --- /dev/null +++ b/source_code/tb/tb_sparce_sasa_table.sv @@ -0,0 +1,484 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_sasa_table.sv +* +* Created by: Vadim Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 08/23/2019 +* Updated by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Last updated: 10/30/2019 +* Description: Testbench for the sasa table file +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + +// macro for getting the index of the sasa table out of all the generated +// tables. i is the index representing the number of table sizes and +// j is the index representing the number of sets +`define GET_IDX(i,j)\ + ((i)*NUM_SETS + (j)) + +// macro to generate the data entry to load into the sasa table. Note that the +// pc is only shifted by 14 bits instead of 16 bits because the datapath PC is +// byte addressed while the sasa table expects word addressed data to save +// space +`define SASA_DATA(pc, rs1, rs2, cond, insts_to_skip)\ + (((pc) << 14) + (((rs1) & 'h1F) << 11) + (((rs2) & 'h1F) << 6) + (((cond) & 'h1) << 5) + ((insts_to_skip) & 'h1F)) + +typedef struct +{ + word_t pc; + word_t sasa_addr; + word_t sasa_data; + word_t preceding_pc; + logic sasa_wen; + logic valid; + logic sasa_enable; + logic[4:0] sasa_rs1; + logic[4:0] sasa_rs2; + logic[4:0] rd; + sasa_cond_t condition; + logic [4:0] insts_to_skip; +} tb_sasa_port_t; + +module tb_sparce_sasa_table (); + + parameter PERIOD = 20; + parameter NUM_TABLE_SIZES = 5; + parameter NUM_SETS = 3; + parameter NUM_SASA_TABLES = NUM_TABLE_SIZES * NUM_SETS; + parameter SASA_ADDR = 32'h90000000; + parameter SASA_CONF_ADDR = SASA_ADDR + 4; + + logic tb_clk; + logic tb_nRST; + + sparce_internal_if sparce_if_arr[NUM_SASA_TABLES](); + tb_sasa_port_t sasa_port_arr[NUM_SASA_TABLES]; + + genvar i, j; + integer tb_i; + integer tb_j; + + generate + begin : tb_variable_sasa + for (i=0; i < NUM_TABLE_SIZES; i++) begin + for (j=0; j < NUM_SETS; j++) begin + sparce_sasa_table #(.SASA_ENTRIES(2**(i+2)),.SASA_SETS(2**j), .SASA_ADDR(SASA_ADDR)) DUT (tb_clk, tb_nRST, sparce_if_arr[`GET_IDX(i,j)]); + assign sparce_if_arr[`GET_IDX(i,j)].pc = sasa_port_arr[`GET_IDX(i,j)].pc; + assign sparce_if_arr[`GET_IDX(i,j)].sasa_addr = sasa_port_arr[`GET_IDX(i,j)].sasa_addr; + assign sparce_if_arr[`GET_IDX(i,j)].sasa_data = sasa_port_arr[`GET_IDX(i,j)].sasa_data; + assign sparce_if_arr[`GET_IDX(i,j)].sasa_wen = sasa_port_arr[`GET_IDX(i,j)].sasa_wen; + assign sparce_if_arr[`GET_IDX(i,j)].sasa_enable = sasa_port_arr[`GET_IDX(i,j)].sasa_enable; + assign sasa_port_arr[`GET_IDX(i,j)].sasa_rs1 = sparce_if_arr[`GET_IDX(i,j)].sasa_rs1; + assign sasa_port_arr[`GET_IDX(i,j)].sasa_rs2 = sparce_if_arr[`GET_IDX(i,j)].sasa_rs2; + assign sasa_port_arr[`GET_IDX(i,j)].insts_to_skip = sparce_if_arr[`GET_IDX(i,j)].insts_to_skip; + assign sasa_port_arr[`GET_IDX(i,j)].preceding_pc = sparce_if_arr[`GET_IDX(i,j)].preceding_pc; + assign sasa_port_arr[`GET_IDX(i,j)].condition = sparce_if_arr[`GET_IDX(i,j)].condition; + assign sasa_port_arr[`GET_IDX(i,j)].valid = sparce_if_arr[`GET_IDX(i,j)].valid; + end + end + end + endgenerate + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + /************************************************************************* + * Initial block; Call tasks here. + *************************************************************************/ + initial begin + tb_clk = 0; + tb_nRST = 1; + for (tb_i=0; tb_i < NUM_TABLE_SIZES ; tb_i++) begin + for (tb_j=0; tb_j < NUM_SETS; tb_j++) begin + $display("Testing size %d table with %d sets", 2**(tb_i+2),2**tb_j); + // test that the table is initialized correctly + test_default_values(tb_i,tb_j); + // test that the table can be loaded correctly + load_sasa_table(tb_i,tb_j); + load_duplicate_entries(tb_i,tb_j); + test_associativity(tb_i,tb_j); + test_lru(tb_i,tb_j); + disable_sasa_table(tb_i,tb_j); + reenable_sasa_table(tb_i, tb_j); + pc_out_of_range(tb_i, tb_j); + end + end + $finish; + end + + /************************************************************************* + * TEST #0: + * Ensure that when initialized, the SASA table outputs every + * entry as not valid + *************************************************************************/ + task test_default_values(integer size_idx, integer set_idx); + integer ii; + integer idx; + initialize(size_idx, set_idx); + idx = `GET_IDX(size_idx, set_idx); + // loop through the sasa table and try to fetch data from each index. + // The table should always output its data as invalid + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].sasa_data = `SASA_DATA(ii << 2, ii, ii, ii, ii); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + assert (sasa_port_arr[idx].valid == 1'b0) else $error("Uninitialized entry in SASA table outputs as valid"); + end + endtask + + /************************************************************************* + * TEST #1: + * Ensure that basic loading of the sasa table functions correctly (and + * consequently that reading loaded values functions properly as well) + *************************************************************************/ + task load_sasa_table(integer size_idx, integer set_idx); + integer ii; + integer idx; + initialize(size_idx, set_idx); + idx = `GET_IDX(size_idx, set_idx); + + @(negedge tb_clk); + + // loop through every possible entry in the sasa table. Because these are + // consecutive tests, there should be no collisions, and every value + // should be readable immediately after writes + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 1); + end + + // after all writes have been completed, ensure that all entries can still + // be read + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 1); + end + endtask + + /************************************************************************* + * TEST #2: + * Ensure when a SASA entry has the same program counter that already exists + * in the SASA table, it will update the existing entry instead of writing to + * the other set. + *************************************************************************/ + task load_duplicate_entries(integer size_idx, integer set_idx); + integer ii; + integer idx; + initialize(size_idx, set_idx); + idx = `GET_IDX(size_idx, set_idx); + + @(negedge tb_clk); + + // write entry to set 1 + write_sasa_entry(size_idx, set_idx, `SASA_DATA('h1000, 1, 2, 3, 4)); + + // loop through every possible entry in the sasa table. Because these are + // consecutive tests, there should be no collisions, and every value + // should be readable immediately after writes + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = 0; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(0, ii, ii, ii, ii)); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(0, ii, ii, ii,ii), 1); + + // make sure that set 1 is not replaced + sasa_port_arr[idx].pc = 'h1000; + read_sasa_entry(size_idx, set_idx, `SASA_DATA('h1000, 1, 2, 3, 4), set_idx != 0); + end + endtask + + /************************************************************************* + * TEST #3: + * Ensures that when the SASA table reaches full capacity, it forces the + * original entry out of the table. + *************************************************************************/ + task test_associativity(integer size_idx, integer set_idx); + integer ii; + integer idx; + word_t pc; + initialize(size_idx, set_idx); + idx = `GET_IDX(size_idx, set_idx); + // Write to all entries, and then keep writing after capacity has been + // reached, forcing the original entries out + for (ii = 0; ii < (2**(size_idx+2)) + ((2**(size_idx+2)) / (2**set_idx)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + // Read the initial entries written in each set, and ensure that they are + // no longer valid (due to being replaced) + for (ii = 0; ii < (2**(size_idx+2)) / (2**set_idx); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii), 0); + end + endtask + + /************************************************************************* + * TEST #4: + * Ensures that when the SASA table reaches full capacity, it forces the LRU + * entry out of the table + *************************************************************************/ + task test_lru(integer size_idx, integer set_idx); + integer ii; + integer idx; + word_t pc; + idx = `GET_IDX(size_idx, set_idx); + + initialize(size_idx, set_idx); + + // write data to every entry in the SASA table + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + // read all data from the first set to reset the LRU + for (ii = 0; ii < (2**(size_idx+2)) / (2**set_idx); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii), 1); + end + // write a set of data to the SASA table + for (ii = (2**(size_idx+2)); ii < (2**(size_idx+2)) + ((2**(size_idx+2)) / (2**set_idx)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + // verify that the data in the first set is still present from the + // original write. + // Note, don't expect valid data from direct-mapped cache configurations + for (ii = 0; ii < (2**(size_idx+2)) / (2**set_idx); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + // set_idx != 0 to avoid expecting data to be present for direct-mapped + // caches + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii), set_idx != 0); + end + endtask + + /************************************************************************* + * TEST #5: + * Ensure that after sasa table is disabled, the outputs are invalid. + *************************************************************************/ + task disable_sasa_table(integer size_idx, integer set_idx); + integer ii; + integer idx; + idx = `GET_IDX(size_idx, set_idx); + initialize(size_idx, set_idx); + + // disable sasa table by writing not 0 to config register + write_to_sasa_config (size_idx, set_idx, `SASA_DATA(0, 0, 0, 0, 7)); + + // write data to every entry in the SASA table + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + + // read all entries and outputs are invalid + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 0); + end + endtask + + /************************************************************************* + * TEST #6: + * Ensure that after sasa table is re-enabled, the outputs are valid. + *************************************************************************/ + task reenable_sasa_table(integer size_idx, integer set_idx); + integer ii; + integer idx; + idx = `GET_IDX(size_idx, set_idx); + initialize(size_idx, set_idx); + + // disable sasa table by writing 1 to config register + write_to_sasa_config (size_idx, set_idx, `SASA_DATA(0, 0, 0, 0, 1)); + + // write data to every entry in the SASA table + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + + // enable sasa table by writing 0 to config register + write_to_sasa_config (size_idx, set_idx, `SASA_DATA(0, 0, 0, 0, 0)); + + // read all entries + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 1); + end + endtask + + /************************************************************************* + * TEST #7: + * Ensure that only PC <= 'hFFFC 0000 can be skipped + *************************************************************************/ + task pc_out_of_range(integer size_idx, integer set_idx); + integer ii; + integer idx; + idx = `GET_IDX(size_idx, set_idx); + initialize(size_idx, set_idx); + + // write data to every entry in the SASA table + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + + // read all entries + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + sasa_port_arr[idx].pc[31:18] = (ii + 1) << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 0); + end + endtask + + /************************************************************************* + * Helper function #1: read sasa entry + *************************************************************************/ + task read_sasa_entry(integer size_idx, integer set_idx, word_t data, logic valid); + integer expected; + integer idx; + idx = `GET_IDX(size_idx, set_idx); + #(PERIOD/20); + if (!valid) begin + assert (sasa_port_arr[idx].valid == '0) else $error("Unitialized entry in SASA table outputs as valid"); + end else begin + assert (sasa_port_arr[idx].valid == '1) else $error("Initialized entry in SASA table outputs as invalid"); + + expected = (data >> 11) & 'h1F; + assert (sasa_port_arr[idx].sasa_rs1 == expected) + else $error("Initialized entry in SASA table outputs incorrect sasa_rs1 value (exp: %d, got: %d)", expected , sasa_port_arr[idx].sasa_rs1); + + expected = (data >> 6) & 'h1F; + assert (sasa_port_arr[idx].sasa_rs2 == expected) + else $error("Initialized entry in SASA table outputs incorrect sasa_rs2 value (exp: %d, got: %d)", expected, sasa_port_arr[idx].sasa_rs2); + + expected = data & 'h1F; + assert (sasa_port_arr[idx].insts_to_skip == expected) + else $error("Initialized entry in SASA table outputs incorrect insts_to_skip value (exp: %d, got: %d)", expected, sasa_port_arr[idx].insts_to_skip); + + assert (sasa_port_arr[idx].preceding_pc == sasa_port_arr[idx].pc) + else $error("Initialized entry in SASA table outputs incorrect preceding_pc value (exp: %d, got: %d)", sasa_port_arr[idx].pc, sasa_port_arr[idx].insts_to_skip); + + expected = (data >> 5) & '1; + assert (sasa_port_arr[idx].condition == sasa_cond_t'(expected)) + else $error("Initialized entry in SASA table outputs incorrect preceding_pc value (exp: %d, got: %d)", sasa_cond_t'(expected), sasa_port_arr[idx].condition); + end + endtask + + /************************************************************************* + * Helper function #2: write sasa entry + *************************************************************************/ + task write_sasa_entry(integer size_idx, integer set_idx, word_t data); + integer idx; + idx = `GET_IDX(size_idx, set_idx); + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '1; + sasa_port_arr[idx].sasa_data = data; + sasa_port_arr[idx].sasa_addr = SASA_ADDR; + sasa_port_arr[idx].sasa_enable = '1; + @(posedge tb_clk); + endtask + + /************************************************************************* + * Helper function #3: initialize signals + *************************************************************************/ + task initialize(integer size_idx, integer set_idx); + integer idx; + idx = `GET_IDX(size_idx, set_idx); + @(negedge tb_clk); + tb_nRST = 0; + sasa_port_arr[idx].pc = '0; + sasa_port_arr[idx].sasa_addr = '0; + sasa_port_arr[idx].sasa_data = '0; + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_enable = '0; + @(negedge tb_clk); + @(negedge tb_clk); + tb_nRST = 1; + @(negedge tb_clk); + endtask + + /************************************************************************* + * Helper function #4: write to configuration register + *************************************************************************/ + task write_to_sasa_config (integer size_idx, integer set_idx, word_t data); + integer idx; + idx = `GET_IDX(size_idx, set_idx); + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '1; + sasa_port_arr[idx].sasa_data = data; + sasa_port_arr[idx].sasa_addr = SASA_CONF_ADDR; + sasa_port_arr[idx].sasa_enable = '1; + @(posedge tb_clk); + endtask + +endmodule + diff --git a/source_code/tb/tb_sparce_sprf.sv b/source_code/tb/tb_sparce_sprf.sv new file mode 100644 index 000000000..c2e3230e3 --- /dev/null +++ b/source_code/tb/tb_sparce_sprf.sv @@ -0,0 +1,188 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_sprf.sv +* +* Created by: Vadim Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 08/18/2019 +* Description: Testbench for the sparsity register file +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + + +typedef struct packed +{ + logic wb_en; + logic [4:0] rd; + logic is_sparse; + logic [4:0] sasa_rs1; + logic [4:0] sasa_rs2; + logic rs1_sparsity; + logic rs2_sparsity; +} sparce_sprf_testvec_t; + +// modport sprf ( +// output rs1_sparsity, rs2_sparsity, +// input wb_en, rd, is_sparse, sasa_rs1, sasa_rs2 +// ); + +module tb_sparce_sprf (); + + parameter PERIOD = 20; + + logic tb_clk; + logic tb_nRST; + + sparce_internal_if sparce_if(); + sparce_sprf DUT(tb_clk, tb_nRST, sparce_if); + + integer i; + sparce_sprf_testvec_t testvec[2:0]; + + assign testvec = + { + {32'b0, 1'b1}, + {32'b1, 1'b0}, + {'1, 1'b0} + }; + + + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + initial begin + tb_clk = 0; + tb_nRST = 1; + i = 0; + initialize; + test_initial_values; + test_writes; + test_no_enable; + test_writes_in_flight; + $finish; + end + + task initialize; + sparce_if.wb_en = 1'b0; + sparce_if.rd = 5'b0; + sparce_if.is_sparse = 1'b0; + sparce_if.sasa_rs1 = 5'b0; + sparce_if.sasa_rs2 = 5'b0; + @(negedge tb_clk); + tb_nRST = 0; + @(negedge tb_clk); + tb_nRST = 1; + endtask + + task test_initial_values; + @(negedge tb_clk); + for (i=0; i< 32; i++) begin + @(negedge tb_clk); + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = 31-i; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == (i == 0)) else $error("SPRF has wrong init values for register %2d", i); + assert (sparce_if.rs2_sparsity == (i == 31)) else $error("SPRF has wrong init values for register %2d", 31-i); + end + endtask + + task test_writes; + initialize; + @(negedge tb_clk); + // Test writes of 1 to the sprf + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 1; + sparce_if.is_sparse = 1; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(negedge tb_clk); + sparce_if.wb_en = 0; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == 1) else $error("SPRF did not store sparse value for register %2d", i); + assert (sparce_if.rs2_sparsity == 1) else $error("SPRF did not store sparse value for register %2d", i); + end + // Test writes of 0 to the sprf + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 1; + sparce_if.is_sparse = 0; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(negedge tb_clk); + sparce_if.wb_en = 0; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == (i==0)) else $error("SPRF did not store non-sparse value for register %2d", i); + assert (sparce_if.rs2_sparsity == (i==0)) else $error("SPRF did not store non-sparse value for register %2d", i); + end + endtask + + task test_no_enable; + initialize; + @(negedge tb_clk); + // Test writes of 1 to the sprf without the enable bit set + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 0; + sparce_if.is_sparse = 1; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(negedge tb_clk); + sparce_if.wb_en = 0; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == (i==0)) else $error("SPRF stored sparse value for register %2d without enable set", i); + assert (sparce_if.rs2_sparsity == (i==0)) else $error("SPRF stored sparse value for register %2d without enable set", i); + end + endtask + + task test_writes_in_flight; + // Test writes of 1 to the sprf in flight + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 1; + sparce_if.is_sparse = 1; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == 1) else $error("SPRF did not process in-flight sparse value for register %2d", i); + assert (sparce_if.rs2_sparsity == 1) else $error("SPRF did not process in-flight sparse value for register %2d", i); + end + // Test writes of 0 to the sprf in flight + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 1; + sparce_if.is_sparse = 0; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == (i==0)) else $error("SPRF did not process in-flight non-sparse value for register %2d", i); + assert (sparce_if.rs2_sparsity == (i==0)) else $error("SPRF did not process in-flight non-sparse value for register %2d", i); + end + endtask + + +endmodule diff --git a/source_code/tb/tb_sparce_svc.sv b/source_code/tb/tb_sparce_svc.sv new file mode 100644 index 000000000..ae2e1111b --- /dev/null +++ b/source_code/tb/tb_sparce_svc.sv @@ -0,0 +1,75 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_svc.sv +* +* Created by: Vadim Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 08/16/2019 +* Description: Testbench for the sparsity value checker +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + + +typedef struct packed +{ + word_t wb_data; + logic is_sparse; +} sparce_svc_testvec_t; + +module tb_sparce_svc (); + + parameter PERIOD = 20; + + sparce_internal_if sparce_if(); + sparce_svc DUT(sparce_if); + + logic tb_clk; + integer i; + sparce_svc_testvec_t testvec[2:0]; + + assign testvec = + { + {32'b0, 1'b1}, + {32'b1, 1'b0}, + {'1, 1'b0} + }; + + + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + initial begin + tb_clk <= 0; + i = 0; + @(posedge tb_clk); + while( i < $size(testvec)) begin + @(negedge tb_clk); + sparce_if.wb_data = testvec[i].wb_data; + @(posedge tb_clk); + assert (sparce_if.is_sparse == testvec[i].is_sparse) else $error("Sparsity not detected properly - Input: %d, Output: %d", testvec[i].wb_data, sparce_if.is_sparse); + i = i+1; + end + $finish; + end + + +endmodule diff --git a/source_code/wscript b/source_code/wscript index d69e74d76..6d6d0a0e6 100644 --- a/source_code/wscript +++ b/source_code/wscript @@ -1,5 +1,5 @@ def configure(ctx): - rvb_srcs = ['caches', 'branch_predictors', 'bus_bridges', 'trackers', 'risc_mgmt', 'privs', 'pipelines', 'standard_core', 'ram'] + rvb_srcs = ['caches', 'branch_predictors', 'bus_bridges', 'trackers', 'risc_mgmt', 'privs', 'pipelines', 'standard_core', 'ram', 'sparce'] ctx.SFFUnits.add('RISCVBusiness_packages', src_dir = ['packages'] ) @@ -75,4 +75,43 @@ def configure(ctx): tb = 'tb_caches', tb_includes = ['include'] ) - + ctx.SFFUnits.add('sparce_svc', + includes = ['include'], + src_dir = ['sparce'], + use = ['RISCVBusiness_packages'], + tb_use = ['RISCVBusiness_packages'], + tb = 'tb_sparce_svc', + tb_includes = ['include'] + ) + ctx.SFFUnits.add('sparce_sprf', + includes = ['include'], + src_dir = ['sparce'], + use = ['RISCVBusiness_packages'], + tb_use = ['RISCVBusiness_packages'], + tb = 'tb_sparce_sprf', + tb_includes = ['include'] + ) + ctx.SFFUnits.add('sparce_sasa_table', + includes = ['include'], + src_dir = ['sparce'], + use = ['RISCVBusiness_packages'], + tb_use = ['RISCVBusiness_packages'], + tb = 'tb_sparce_sasa_table', + tb_includes = ['include'] + ) + ctx.SFFUnits.add('sparce_psru', + includes = ['include'], + src_dir = ['sparce'], + use = ['RISCVBusiness_packages'], + tb_use = ['RISCVBusiness_packages'], + tb = 'tb_sparce_psru', + tb_includes = ['include'] + ) + ctx.SFFUnits.add('sparce_cfid', + includes = ['include'], + src_dir = ['sparce'], + use = ['RISCVBusiness_packages'], + tb_use = ['RISCVBusiness_packages'], + tb = 'tb_sparce_cfid', + tb_includes = ['include'] + ) diff --git a/sparce.yml b/sparce.yml new file mode 100644 index 000000000..d1a4283ff --- /dev/null +++ b/sparce.yml @@ -0,0 +1,26 @@ +# ISA Configurations +isa_params: + xlen : 32 + +# Microarchitectural Configurations +microarch_params: + # Branch/Jump Configurations + br_predictor_type : "not_taken" + + # Cache configurations + cache_config : "separate" + dcache_type : "pass_through" + icache_type : "pass_through" + + # Bus configurations + bus_endianness : "big" + bus_interface_type : "generic_bus_if" + + # Sparisty Optimizations + sparce_enabled : "enabled" + +# RISC-MGMT Extension Configuration +risc_mgmt_params: + standard_extensions: + nonstandard_extensions: + diff --git a/verification/asm-env/selfasm/riscv_test.h b/verification/asm-env/selfasm/riscv_test.h index 4466f9989..8e26e16a6 100644 --- a/verification/asm-env/selfasm/riscv_test.h +++ b/verification/asm-env/selfasm/riscv_test.h @@ -66,8 +66,7 @@ done: \ j done - -#endif +#endif // merged in //----------------------------------------------------------------------- // Pass/Fail Macro @@ -87,6 +86,15 @@ j done +//----------------------------------------------------------------------- +// End Macro +//----------------------------------------------------------------------- + +//#define RVTEST_CODE_END \ +//ecall: ecall; \ +// j ecall + + //----------------------------------------------------------------------- // Data Section Macro //----------------------------------------------------------------------- diff --git a/verification/asm-env/selfasm/riscv_test.h.old b/verification/asm-env/selfasm/riscv_test.h.old deleted file mode 100644 index 22c1b833f..000000000 --- a/verification/asm-env/selfasm/riscv_test.h.old +++ /dev/null @@ -1,184 +0,0 @@ -// See LICENSE for license details. - -#ifndef _ENV_PHYSICAL_SINGLE_CORE_H -#define _ENV_PHYSICAL_SINGLE_CORE_H - -#include "../encoding.h" - -//----------------------------------------------------------------------- -// Begin Macro -//----------------------------------------------------------------------- - -#define RVTEST_RV64U \ - .macro init; \ - .endm - -#define RVTEST_RV64UF \ - .macro init; \ - RVTEST_FP_ENABLE; \ - .endm - -#define RVTEST_RV32U \ - .macro init; \ - .endm - -#define RVTEST_RV32UF \ - .macro init; \ - RVTEST_FP_ENABLE; \ - .endm - -#define RVTEST_RV64M \ - .macro init; \ - RVTEST_ENABLE_MACHINE; \ - .endm - -#define RVTEST_RV64S \ - .macro init; \ - RVTEST_ENABLE_SUPERVISOR; \ - .endm - -#define RVTEST_RV64SV \ - .macro init; \ - RVTEST_ENABLE_SUPERVISOR; \ - .endm - -#define RVTEST_RV32M \ - .macro init; \ - RVTEST_ENABLE_MACHINE; \ - .endm - -#define RVTEST_RV32S \ - .macro init; \ - RVTEST_ENABLE_SUPERVISOR; \ - .endm - -#ifdef __riscv64 -# define CHECK_XLEN csrr a0, mcpuid; bltz a0, 1f; RVTEST_PASS; 1: -#else -# define CHECK_XLEN csrr a0, mcpuid; bgez a0, 1f; RVTEST_PASS; 1: -#endif - -#define RVTEST_ENABLE_SUPERVISOR \ - li a0, MSTATUS_PRV1 & (MSTATUS_PRV1 >> 1); \ - csrs mstatus, a0; \ - -#define RVTEST_ENABLE_MACHINE \ - li a0, MSTATUS_PRV1; \ - csrs mstatus, a0; \ - -#define RVTEST_FP_ENABLE \ - li a0, MSTATUS_FS & (MSTATUS_FS >> 1); \ - csrs mstatus, a0; \ - csrwi fcsr, 0 - -#define RISCV_MULTICORE_DISABLE \ - csrr a0, mhartid; \ - 1: bnez a0, 1b - -#define EXTRA_TVEC_USER -#define EXTRA_TVEC_SUPERVISOR -#define EXTRA_TVEC_HYPERVISOR -#define EXTRA_TVEC_MACHINE -#define EXTRA_INIT -#define EXTRA_INIT_TIMER - -#define RVTEST_CODE_BEGIN \ - .text; \ - .align 6; \ - .weak stvec_handler; \ - .weak mtvec_handler; \ -tvec_user: \ - EXTRA_TVEC_USER; \ - /* test whether the test came from pass/fail */ \ - la t5, ecall; \ - csrr t6, mepc; \ - beq t5, t6, write_tohost; \ - /* test whether the stvec_handler target exists */ \ - la t5, stvec_handler; \ - bnez t5, mrts_routine; \ - /* test whether the mtvec_handler target exists */ \ - la t5, mtvec_handler; \ - bnez t5, mtvec_handler; \ - /* some other exception occurred */ \ - j other_exception; \ - .align 6; \ -tvec_supervisor: \ - EXTRA_TVEC_SUPERVISOR; \ - csrr t5, mcause; \ - bgez t5, tvec_user; \ - mrts_routine: \ - mrts; \ - .align 6; \ -tvec_hypervisor: \ - EXTRA_TVEC_HYPERVISOR; \ - /* renting some space out here */ \ - other_exception: \ - 1: ori TESTNUM, TESTNUM, 1337; /* some other exception occurred */ \ - write_tohost: \ - csrw mtohost, TESTNUM; \ - j write_tohost; \ - .align 6; \ -tvec_machine: \ - EXTRA_TVEC_MACHINE; \ - la t5, ecall; \ - csrr t6, mepc; \ - beq t5, t6, write_tohost; \ - la t5, mtvec_handler; \ - bnez t5, mtvec_handler; \ - j other_exception; \ - .align 6; \ - .globl _start; \ -_start: \ - RISCV_MULTICORE_DISABLE; \ - CHECK_XLEN; \ - li TESTNUM, 0; \ - la t0, stvec_handler; \ - beqz t0, 1f; \ - csrw stvec, t0; \ -1: li t0, MSTATUS_PRV1 | MSTATUS_PRV2 | MSTATUS_IE1 | MSTATUS_IE2; \ - csrc mstatus, t0; \ - init; \ - EXTRA_INIT; \ - EXTRA_INIT_TIMER; \ - la t0, 1f; \ - csrw mepc, t0; \ - csrr a0, mhartid; \ - eret; \ -1: - -//----------------------------------------------------------------------- -// End Macro -//----------------------------------------------------------------------- - -#define RVTEST_CODE_END \ -ecall: ecall; \ - j ecall - -//----------------------------------------------------------------------- -// Pass/Fail Macro -//----------------------------------------------------------------------- - -#define RVTEST_PASS \ - fence; \ - li TESTNUM, 1; \ - j ecall - -#define TESTNUM x28 -#define RVTEST_FAIL \ - fence; \ -1: beqz TESTNUM, 1b; \ - sll TESTNUM, TESTNUM, 1; \ - or TESTNUM, TESTNUM, 1; \ - j ecall - -//----------------------------------------------------------------------- -// Data Section Macro -//----------------------------------------------------------------------- - -#define EXTRA_DATA - -#define RVTEST_DATA_BEGIN EXTRA_DATA .align 4; .global begin_signature; begin_signature: -#define RVTEST_DATA_END .align 4; .global end_signature; end_signature: - - -#endif diff --git a/verification/asm-env/selfasm/test_macros.h b/verification/asm-env/selfasm/test_macros.h index 778dc82c4..6bab5abb8 100644 --- a/verification/asm-env/selfasm/test_macros.h +++ b/verification/asm-env/selfasm/test_macros.h @@ -7,6 +7,8 @@ #----------------------------------------------------------------------- # Helper macros #----------------------------------------------------------------------- +#define SPARCE_ADDR 0x90000000 +#define SPARCE_CONFIG_ADDR (SPARCE_ADDR + 4) #define TEST_CASE( testnum, testreg, correctval, code... ) \ test_ ## testnum: \ @@ -15,6 +17,27 @@ test_ ## testnum: \ li TESTNUM, testnum; \ bne testreg, x29, fail; +#define SASA_COND_OR 0 +#define SASA_COND_AND 1 + +#define CALC_SASA_VAL(reg1, reg2, condition, insts_to_skip) \ +(((reg1) << 11) + ((reg2) << 6) + ((condition << 5)) + ((insts_to_skip))); + +#define STORE_SASA_FROM_MEM(mem_label, reg_offset, tempreg1, tempreg2) \ +slli tempreg1, reg_offset, 3; \ +la tempreg2, mem_label; \ +add tempreg2, tempreg2, tempreg1; \ +lw tempreg1, 0(tempreg2); \ +slli tempreg1, tempreg1, 14; \ +lw tempreg2, 4(tempreg2); \ +or tempreg1, tempreg1, tempreg2; \ +li tempreg2, SPARCE_ADDR; \ +sw tempreg1, 0(tempreg2); + +#define CREATE_SASA_MEMORY_ENTRIES(skip_label, reg1, reg2, condition, insts_to_skip) \ +.word skip_label; \ +.word CALC_SASA_VAL(reg1, reg2, condition, insts_to_skip); \ + # We use a macro hack to simpify code generation for various numbers # of bubble cycles. diff --git a/verification/asm-tests/RV32I/mult.S b/verification/asm-tests/RV32I/mult.S new file mode 100755 index 000000000..f00875d30 --- /dev/null +++ b/verification/asm-tests/RV32I/mult.S @@ -0,0 +1,63 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mult.S +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: 04/17/2019 +* Description: Function that multiplies 2 numbers +*/ + +#include "riscv_test.h" + +RVTEST_DATA_DUMP_BEGIN + +RVTEST_INTVEC_USER_BEGIN + nop +RVTEST_INTVEC_SUPER_BEGIN + nop +RVTEST_INTVEC_HYPER_BEGIN + nop +RVTEST_INTVEC_MACH_BEGIN + nop + +RVTEST_CODE_BEGIN + +main: + la x5, tdat0 + ori x1, x0, 0x03 // numbers to multiply + ori x2, x0, 0x06 + +mult: + andi x3, x2, 1 // if reg2 is odd + beq x3, x0, shift + add x4, x4, x1 // solution stored in reg4 + +shift: + slli x1, x1, 1 + srli x2, x2, 1 + bne x2, x0, mult + sw x4, 0(x5) + +RVTEST_CODE_END + +.data +#test data goes here +tdat0: .word 0xdeadcafe + +RVTEST_DATA_DUMP_END + diff --git a/verification/asm-tests/RV32I/sparce.S b/verification/asm-tests/RV32I/sparce.S new file mode 100755 index 000000000..c73bdadfd --- /dev/null +++ b/verification/asm-tests/RV32I/sparce.S @@ -0,0 +1,59 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce.S +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: 04/07/2019 +* Description: Preliminary test for sparcity +*/ + +#include "riscv_test.h" + +RVTEST_DATA_DUMP_BEGIN + +RVTEST_INTVEC_USER_BEGIN + nop +RVTEST_INTVEC_SUPER_BEGIN + nop +RVTEST_INTVEC_HYPER_BEGIN + nop +RVTEST_INTVEC_MACH_BEGIN + nop + +RVTEST_CODE_BEGIN + +main: + ori x1, x0, 0xf0 + ori x2, x0, 0x55 + ori x3, x0, 0x00 + ori x5, x0, 0x00 + + // generate sparse and nonsparse values + and x6, x1, x2 + and x7, x1, x3 + + or x8, x3, x5 + or x9, x3, x1 + +RVTEST_CODE_END + + +RVTEST_DATA_DUMP_END + + + diff --git a/verification/self-tests/RV32I/sparce_ctrlflow_nottaken.S b/verification/self-tests/RV32I/sparce_ctrlflow_nottaken.S new file mode 100644 index 000000000..8b18a9c31 --- /dev/null +++ b/verification/self-tests/RV32I/sparce_ctrlflow_nottaken.S @@ -0,0 +1,67 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_ctrlflow_nottaken.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction AT the SASA preceding PC will be +# executed but does not affect the skipping conditions. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop + +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + bne x0, x0, sasa_test_case_end_fail + j skip_point_1 + j sasa_test_case_end_fail + +skip_point_1: # SASA_AND: 0,0 -> skip 2 instructions + beq x5, x0, sasa_test_case_end_fail + j skip_point_2 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_2: # SASA_OR: 0,0 -> skip 2 instructions + blt x5, x0, sasa_test_case_end_fail + j sasa_test_case_end + j sasa_test_case_end_fail + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_ctrlflow_taken.S b/verification/self-tests/RV32I/sparce_ctrlflow_taken.S new file mode 100644 index 000000000..401913b9a --- /dev/null +++ b/verification/self-tests/RV32I/sparce_ctrlflow_taken.S @@ -0,0 +1,88 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_ctrlflow_taken.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction AT the SASA preceding PC will be +# executed but does not affect the skipping conditions. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro +# x8 = temp reg + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop +la x5, skip_point_2 # load return addr + +ori x8, x0, 7 +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + j skip_point_1 # jump and skip have same destination; jump has priority + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_1: # SASA_AND: 0,0 -> skip 2 instructions + jalr x5 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_2: # SASA_OR: 0,0 -> skip 2 instructions: + beq x0, x0, skip_point_3 # branch and skip have same destination + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_3: # SASA_OR: 0,0 -> skip 2 instructions: + bne x0, x8, skip_point_4 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_4: # SASA_OR: 0,0 -> skip 2 instructions: + blt x0, x8, sasa_test_case_end # branch and skip have same destination + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_3, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_4, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_inflight_execute.S b/verification/self-tests/RV32I/sparce_inflight_execute.S new file mode 100644 index 000000000..60ceeb125 --- /dev/null +++ b/verification/self-tests/RV32I/sparce_inflight_execute.S @@ -0,0 +1,83 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_inflight_execute.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction BEFORE the SASA preceding PC will be +# executed and forwarded from the execute stage (e.i. it will affect the +# skipping condition). +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 1 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop + +ori x3, x0, 0 # this line will execute and affect the skip conditions +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + nop + j sasa_test_case_end_fail + j sasa_test_case_end_fail + bne x0, x3, sasa_test_case_end_fail + +ori x4, x0, 1 # this line will execute and affect the skip conditions +skip_point_1: # SASA_AND: 0,1 -> NO skipping + bne x0, x4, checkpoint_0 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +checkpoint_0: + ori x3, x0, 1 # this line will execute and affect the skip conditions +skip_point_2: # SASA_OR: 1,1 -> NO skipping + bne x0, x4, checkpoint_1 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +checkpoint_1: + ori x3, x0, 0 # this line will execute and affect the skip conditions +skip_point_3: # SASA_OR: 0,1 -> skip 2 instructions + nop + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end + + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_3, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_inflight_fetch.S b/verification/self-tests/RV32I/sparce_inflight_fetch.S new file mode 100644 index 000000000..56f51d67c --- /dev/null +++ b/verification/self-tests/RV32I/sparce_inflight_fetch.S @@ -0,0 +1,85 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_inflight_fetch.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction AT the SASA preceding PC will be +# executed but does not affect the skipping conditions. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop + +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + ori x3, x0, 1 # this line will execute but has no effect on the skip conditions + # even though x3 is a condition register + j sasa_test_case_end_fail + j sasa_test_case_end_fail + beq x0, x3, sasa_test_case_end_fail + +skip_point_1: # SASA_AND: 1,0 -> NO skipping + ori x3, x0, 0 # this line will execute but has no effect on the skip conditions + # even though x3 is a condition register + beq x0, x3, skip_point_2 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_2: # SASA_OR: 0,0 -> skip 2 instructions + ori x3, x0, 1 # this line will execute but has no effect on the skip conditions + # even though x3 is a condition register + j sasa_test_case_end_fail + j sasa_test_case_end_fail + ori x4, x0, 1 + beq x0, x3, sasa_test_case_end_fail + +skip_point_3: # SASA_OR: 1,1 -> NO skipping + ori x3, x0, 0 # this line will execute but has no effect on the skip conditions + # even though x3 is a condition register + beq x0, x3, sasa_test_case_end + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_3, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_sasa_disable.S b/verification/self-tests/RV32I/sparce_sasa_disable.S new file mode 100644 index 000000000..1c5597674 --- /dev/null +++ b/verification/self-tests/RV32I/sparce_sasa_disable.S @@ -0,0 +1,88 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_sasa_disable.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction AT the SASA preceding PC will be +# executed but does not affect the skipping conditions. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro +# x8 = temp regs to store SASA_CONFIG_ADDR + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +nop +nop +nop + +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + nop + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +li x8, SPARCE_CONFIG_ADDR +sw t0, 0(x8) # disable SASA table + +# try storing to SASA table after disabled +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) + +skip_point_1: # SASA_AND: 0,0 -> skip 2 instructions but DISABLED + nop + j skip_point_2 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_2: # SASA_OR: 0,0 -> skip 2 instructions but DISABLED + nop + sw x0, 0(x8) # enable SASA table + j skip_point_3 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_3: # SASA_OR: 0,0 -> skip 2 instructions + nop + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_3, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_single_skip.S b/verification/self-tests/RV32I/sparce_single_skip.S new file mode 100644 index 000000000..193718ba8 --- /dev/null +++ b/verification/self-tests/RV32I/sparce_single_skip.S @@ -0,0 +1,50 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_single_skip.S +#----------------------------------------------------------------------------- +# +# This is the most basic test for the SparCE optimizations. An entry is loaded +# into the SASA table, and the conditions for skipping are met. The test fails +# when any of the instructions to be skipped are executed, and passes otherwise. +# +# Register 5 is a word offset value from address label 'sasa_entry_0'. +# This is a parameter required by the STORE_SASA_FROM_MEM. +# This parameter should skip the first four SASA data constructed +# by CREATE_SASA_MEMORY_ENTRIES at the bottom. + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori x5, x0, 5 # offset +STORE_SASA_FROM_MEM(sasa_entry_0, x5, x1, x2) +nop +nop +skip_point: + nop + li TESTNUM, 0 + li TESTNUM, 0 + li TESTNUM, 0 + li TESTNUM, 0 + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 4) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_skip_range.S b/verification/self-tests/RV32I/sparce_skip_range.S new file mode 100644 index 000000000..2b6240efb --- /dev/null +++ b/verification/self-tests/RV32I/sparce_skip_range.S @@ -0,0 +1,138 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_skip_range.S +#----------------------------------------------------------------------------- +# +# This test ensures that each skip range works for the sparce module. +# The block of CREATE_SASA_MEMORY_ENTRIES at the bottom first constructs +# the SASA data. Then for every loop the STORE_SASA_FROM_MEM macro +# stores one entry to the SASA table. +# +# When the PC reaches 'skip_point', it skips x instructions based on the +# SASA entry. Then JAL to 'check_skip_range' subroutine to check if it +# is skipping from the expected address by reading the ra register. +# +# Register usage: +# x1 (ra) = return address +# x3, x4 = input regs to SASA entries +# x5 (t0) = loop counter/jump range (0 to 31) +# x6, x7 (t1, t2) = temp regs passed to macro +# x8 = addr of the start of skip region +# x9 = number of test cases (start from 0) +# x10 = temp + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 # loop counter (0 to 31) +ori x9, x0, 31 # number of test cases (start from 0) +la x8, skip_point # loads addr of skip_point + +loop_start: +blt x9, t0, sasa_test_case_end # end test if num of test cases < loop_counter + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) # creates new SASA entry for the same PC + # but +1 skip range for every loop +nop +nop +skip_point: + nop + jal check_skip_range # skip 0 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 5 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 10 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 15 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 20 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 25 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 31 instructions + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +check_skip_range: + slli x10, t0, 2 # x10 = (loop_counter * 4) + addi ra, ra, -8 # ra - 8 - x10 + sub ra, ra, x10 + addi t0, t0, 1 # increment loop counter + beq ra, x8, loop_start # expects ra - 4 - (loop_counter * 4) = skip_point + + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 0) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 1) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 3) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 4) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 5) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 6) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 7) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 8) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 9) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 10) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 11) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 12) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 13) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 14) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 15) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 16) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 17) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 18) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 19) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 20) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 21) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 22) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 23) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 24) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 25) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 26) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 27) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 28) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 29) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 30) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 31) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_sprf_uninitialized.S b/verification/self-tests/RV32I/sparce_sprf_uninitialized.S new file mode 100644 index 000000000..11ea9eedf --- /dev/null +++ b/verification/self-tests/RV32I/sparce_sprf_uninitialized.S @@ -0,0 +1,57 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_sprf_uninitialized.S +#----------------------------------------------------------------------------- +# +# This test ensures that skipping does not occur when registers have not been +# initialized after reset. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop + +skip_point_0: # SASA_AND: X,X -> NO skipping + nop + j skip_point_1 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_1: # SASA_OR: X,X -> NO skipping + nop + j sasa_test_case_end + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END From a432ac266b0371df0ff5b309c0c2a621249b5df5 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sat, 10 Oct 2020 18:08:10 -0400 Subject: [PATCH 02/47] Changed TBs to use internal halt signal due to removal of external halt signal. Tests all pass IF: 1. RESET_PC is changed to 0x8000_0000 2. In the sparce SASA Table, the constraint on the skipping (pc[31:18] == '0) is removed to allow PC 0x8000_0000+ to skip. --- source_code/tb/tb_RISCVBusiness.sv | 2 +- source_code/tb/tb_RISCVBusiness_self_test.sv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source_code/tb/tb_RISCVBusiness.sv b/source_code/tb/tb_RISCVBusiness.sv index c0388276b..cc44d838a 100644 --- a/source_code/tb/tb_RISCVBusiness.sv +++ b/source_code/tb/tb_RISCVBusiness.sv @@ -145,7 +145,7 @@ module tb_RISCVBusiness (); nRST = 1; - while (halt == 0 && clk_count != `RVB_CLK_TIMEOUT) begin + while (DUT.halt == 0 && clk_count != `RVB_CLK_TIMEOUT) begin @(posedge CLK); clk_count++; if (gen_bus_if.addr == 16'h0000 & !gen_bus_if.busy & gen_bus_if.wen) diff --git a/source_code/tb/tb_RISCVBusiness_self_test.sv b/source_code/tb/tb_RISCVBusiness_self_test.sv index fe983e616..655fa3576 100644 --- a/source_code/tb/tb_RISCVBusiness_self_test.sv +++ b/source_code/tb/tb_RISCVBusiness_self_test.sv @@ -142,7 +142,7 @@ module tb_RISCVBusiness_self_test (); nRST = 1; - while (halt == 0 && clk_count != `RVBSELF_CLK_TIMEOUT) begin + while (DUT.halt == 0 && clk_count != `RVBSELF_CLK_TIMEOUT) begin @(posedge CLK); clk_count++; if(gen_bus_if.addr == 16'h0000 & !gen_bus_if.busy & gen_bus_if.wen) begin From 03979a54ff274ca959a17ad1020406371f428c3a Mon Sep 17 00:00:00 2001 From: Enes Shaltami <31215785+Enes-Shaltami@users.noreply.github.com> Date: Tue, 20 Oct 2020 20:21:42 -0400 Subject: [PATCH 03/47] Interrupt integration (#2) Merging Enes' priv unit updates. Works for all existing ASM tests (made to work with it). --- .gitignore | 4 + example.yml | 3 + run_tests.py | 4 +- source_code/include/core_interrupt_if.vh | 26 ++ source_code/include/priv_1_11_internal_if.vh | 75 +++-- .../packages/machine_mode_types_1_11_pkg.sv | 160 +++++----- .../privs/priv_1_11/priv_1_11_block.sv | 55 +++- .../privs/priv_1_11/priv_1_11_control.sv | 139 ++++++--- .../privs/priv_1_11/priv_1_11_csr_rfile.sv | 215 +++++--------- .../priv_1_11/priv_1_11_pipeline_control.sv | 42 ++- source_code/privs/priv_wrapper.sv | 4 +- source_code/privs/wscript | 4 +- source_code/standard_core/RISCVBusiness.sv | 7 +- source_code/tb/tb_RISCVBusiness.sv | 12 +- source_code/tb/tb_RISCVBusiness_self_test.sv | 11 +- .../tb/tb_RISCVBusiness_self_test_plic.sv | 278 ++++++++++++++++++ source_code/tb/tb_priv_1_11_block.sv | 201 +++++++++++++ source_code/tb/tb_priv_1_11_control.sv | 198 +++++++++++++ source_code/wscript | 26 ++ verification/interrupt-tests/ext_interrupt.S | 171 +++++++++++ verification/interrupt-tests/interrupts.S | 52 ++++ .../soft_interrupt.S} | 44 +-- verification/self-tests/RV32I/.link.ld.swp | Bin 12288 -> 0 bytes verification/self-tests/RV32I/misa.S | 37 +++ verification/self-tests/RV32I/timer.S | 7 +- 25 files changed, 1410 insertions(+), 365 deletions(-) create mode 100644 source_code/include/core_interrupt_if.vh create mode 100644 source_code/tb/tb_RISCVBusiness_self_test_plic.sv create mode 100644 source_code/tb/tb_priv_1_11_block.sv create mode 100644 source_code/tb/tb_priv_1_11_control.sv create mode 100644 verification/interrupt-tests/ext_interrupt.S create mode 100644 verification/interrupt-tests/interrupts.S rename verification/{self-tests/RV32I/timer2.S => interrupt-tests/soft_interrupt.S} (71%) delete mode 100644 verification/self-tests/RV32I/.link.ld.swp create mode 100644 verification/self-tests/RV32I/misa.S diff --git a/.gitignore b/.gitignore index 6bfc103d6..7bb588ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ source_code/fpga/output_files *.lock-waf* # Ignore files for commands for moving stuff around *.cmd +mitll* +# Ignore temporary files created by gedit +*~ +open_files.sh diff --git a/example.yml b/example.yml index 6355d31f7..af4c4da92 100644 --- a/example.yml +++ b/example.yml @@ -16,6 +16,9 @@ microarch_params: bus_endianness : "big" bus_interface_type : "generic_bus_if" + # Sparisty Optimizations + sparce_enabled : "disabled" + # RISC-MGMT Extension Configuration risc_mgmt_params: standard_extensions: diff --git a/run_tests.py b/run_tests.py index e152209dc..f4c4a7990 100755 --- a/run_tests.py +++ b/run_tests.py @@ -44,7 +44,7 @@ # Change this variable to the filename (minus extension) # of the top level file for your project. This should # match the file name given in the top level wscript -TOP_LEVEL = "RISCVBusiness" +TOP_LEVEL = "RISCVBusiness" # NOTE: Adjust this module name to adjust what top level is needed for the Cadence simulation def parse_arguments(): global ARCH, FILE_NAME, SUPPORTED_ARCHS, TEST_TYPE @@ -601,7 +601,7 @@ def run_selfasm(): print "Starting self tests..." for f in files: # TODO: Fix timer error - if 'timer' in f: continue + #if 'timer2' in f: continue if 'asicfab' in os.environ['HOSTNAME']: # Do work remotely diff --git a/source_code/include/core_interrupt_if.vh b/source_code/include/core_interrupt_if.vh new file mode 100644 index 000000000..bd3217734 --- /dev/null +++ b/source_code/include/core_interrupt_if.vh @@ -0,0 +1,26 @@ +`ifndef CORE_INTERRUPT_IF +`define CORE_INTERRUPT_IF + + interface core_interrupt_if(); + logic ext_int, ext_int_clear; + logic soft_int, soft_int_clear; + logic timer_int, timer_int_clear; + + modport core( + input ext_int, soft_int, timer_int, + ext_int_clear, soft_int_clear, timer_int_clear + ); + + modport plic( + output ext_int, ext_int_clear + ); + + modport clint( + output soft_int_clear, timer_int_clear, + soft_int, timer_int + ); + + endinterface + + +`endif diff --git a/source_code/include/priv_1_11_internal_if.vh b/source_code/include/priv_1_11_internal_if.vh index c20177331..8c3f01622 100644 --- a/source_code/include/priv_1_11_internal_if.vh +++ b/source_code/include/priv_1_11_internal_if.vh @@ -27,66 +27,87 @@ `include "component_selection_defines.vh" -interface priv_1_11_internal_if; +interface priv_1_11_internal_if; // also labeled as prv_intern_if in most modules import machine_mode_types_1_11_pkg::*; import rv32i_types_pkg::*; - logic mip_rup; - logic mtval_rup; - logic mcause_rup; - logic mepc_rup; - logic mstatus_rup; - logic clear_timer_int; - logic intr; - logic pipe_clear; - logic ret; - logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s; - logic breakpoint, env_m, timer_int, soft_int, ext_int; - logic insert_pc; - logic swap, clr, set; - logic valid_write, invalid_csr; - logic instr_retired; + // Machine registers are being ruptured (activated) to denote when to change the value of this register for hardware + logic mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup; + logic intr; // denote whether an exception or interrupt register + logic pipe_clear; // e_ex_stage is where you check what type of hazard unit instruction you are receiving. Simply, checking whether or not the pipeline is clear of any hazards + logic mret, sret, uret; //returns after handling a trap instruction + + + // sources for interrupts + logic timer_int_u, timer_int_s, timer_int_m; + logic soft_int_u, soft_int_s, soft_int_m; + logic ext_int_u, ext_int_s, ext_int_m; + logic reserved_0, reserved_1, reserved_2; + + // signals to clear the pending interrupt + logic clear_timer_int_u, clear_timer_int_s, clear_timer_int_m; + logic clear_soft_int_u, clear_soft_int_s, clear_soft_int_m; + logic clear_ext_int_u, clear_ext_int_s, clear_ext_int_m; + + + // sources for exceptions + logic mal_insn, fault_insn_access, illegal_insn, breakpoint, fault_l, mal_l, fault_s, mal_s; + logic env_u, env_s, env_m, fault_insn_page, fault_load_page, fault_store_page; + + logic insert_pc; // inform pipeline that the pc will need to be changed. either when an instruction is a ret instruction, or pipeline is clear and a proper instruction + logic swap, clr, set; // activated for CSR Assembly instructions + + + logic valid_write, invalid_csr; // valid write occurs with an r type instruction that does not have any pipeline stalls; invalid_csr + logic instr_retired; // instruction is done (retired) when there is a write back enable and there is a proper instruction // RISC-MGMT logic ex_rmgmt; logic [$clog2(`NUM_EXTENSIONS)-1:0] ex_rmgmt_cause; - word_t epc, mtval, priv_pc; - word_t [3:0] xtvec, xepc_r; + word_t epc; // pc of the instruction prior to the exception + word_t priv_pc; // pc that would need to be changed for pipeline word_t wdata, rdata; + word_t mtval; mip_t mip, mip_next; mtval_t mtval_next; mcause_t mcause, mcause_next; - mepc_t mepc, mepc_next; + mepc_t mepc, mepc_next; // holds pc of interrupted instruction mstatus_t mstatus, mstatus_next; mtvec_t mtvec; mie_t mie; - csr_addr_t addr; + csr_addr_t addr; // 12-bit address for CSR instructions modport csr ( input mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup, mip_next, mtval_next, mcause_next, mepc_next, mstatus_next, swap, clr, set, wdata, addr, valid_write, instr_retired, - output mtvec, mepc, mie, timer_int, mip, mcause, mstatus, clear_timer_int, - rdata, invalid_csr, xtvec, xepc_r + output mtvec, mepc, mie, mip, mcause, mstatus, + rdata, invalid_csr ); modport prv_control ( output mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup, mip_next, mcause_next, mepc_next, mstatus_next, mtval_next, intr, - input mepc, mie, mip, mcause, mstatus, clear_timer_int, pipe_clear, ret, - epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m, timer_int, soft_int, ext_int, mtval, ex_rmgmt, - ex_rmgmt_cause + input mepc, mie, mip, mcause, mstatus, clear_timer_int_m, clear_ext_int_m, clear_soft_int_m, + clear_timer_int_u, clear_ext_int_u, clear_soft_int_u, clear_timer_int_s, clear_ext_int_s, + clear_soft_int_s, pipe_clear, mret, epc, fault_insn_access, mal_insn, illegal_insn, fault_l, + mal_l, fault_s, mal_s, breakpoint, env_m, env_s, env_u, fault_insn_page, fault_load_page, + fault_store_page, timer_int_u, timer_int_s, timer_int_m, soft_int_u, soft_int_s, soft_int_m, + ext_int_u, ext_int_s, ext_int_m, mtval, ex_rmgmt, ex_rmgmt_cause ); modport pipe_ctrl ( - input intr, ret, pipe_clear, xtvec, xepc_r, + input intr, mret, pipe_clear, mtvec, mcause, mepc, output insert_pc, priv_pc ); + modport tb ( + output ext_int_m, clear_ext_int_m + ); + endinterface `endif // PRIV_1_11_INTERNAL_IF_VH diff --git a/source_code/packages/machine_mode_types_1_11_pkg.sv b/source_code/packages/machine_mode_types_1_11_pkg.sv index e3c7b8074..1d44b836d 100644 --- a/source_code/packages/machine_mode_types_1_11_pkg.sv +++ b/source_code/packages/machine_mode_types_1_11_pkg.sv @@ -40,7 +40,6 @@ package machine_mode_types_1_11_pkg; MIDELEG_ADDR = 12'h303, MIE_ADDR = 12'h304, MTVEC_ADDR = 12'h305, - //TODO: MCOUNTEREN AND ASSOCIATED REGS/CONTGROL (e.g. cycle/time) MCOUNTEREN_ADDR = 12'h306, MSCRATCH_ADDR = 12'h340, @@ -49,38 +48,12 @@ package machine_mode_types_1_11_pkg; MTVAL_ADDR = 12'h343, MIP_ADDR = 12'h344, - // TODO: MAY BE ABLE TO REMOVE BELOW - MBASE_ADDR = 12'h380, - MBOUND_ADDR = 12'h381, - MIBASE_ADDR = 12'h382, - MIBOUND_ADDR = 12'h383, - MDBASE_ADDR = 12'h384, - MDBOUND_ADDR = 12'h385, - // TODO: MAY BE ABLE TO REMOVE ABOVE - // TODO: BELOW MUST BE REMOVED - MTOHOST_ADDR = 12'h780, - MFROMHOST_ADDR = 12'h781, - HTIMEW_ADDR = 12'hB01, - HTIMEHW_ADDR = 12'hB81, - MTIMECMP_ADDR = 12'h321, - MTIME_ADDR = 12'h701, - MTIMEH_ADDR = 12'h741, - // TODO: ABOVE MUST BE REMOVED - MCYCLE_ADDR = 12'hB00, MINSTRET_ADDR = 12'hB02, MCYCLEH_ADDR = 12'hB80, MINSTRETH_ADDR = 12'hB82 } csr_addr_t; - /* Priv Levels */ - typedef enum logic [1:0] { - U_MODE = 2'b00, - S_MODE = 2'b01, - H_MODE = 2'b10, - M_MODE = 2'b11 - } prv_lvl_t; - /* Machine Mode Register Types */ /* misaid types */ @@ -126,17 +99,6 @@ package machine_mode_types_1_11_pkg; /* mstatus types */ - typedef enum logic [4:0] { - VM_MBARE = 5'h0, - VM_MBB = 5'h1, - VM_MBBID = 5'h2, - VM_SV32 = 5'h8, - VM_SV39 = 5'h9, - VM_SV48 = 5'ha, - VM_SV57 = 5'hb, - VM_SV64 = 5'hc - } vm_t; - typedef enum logic [1:0] { FS_OFF = 2'h0, FS_INITIAL = 2'h1, @@ -150,48 +112,80 @@ package machine_mode_types_1_11_pkg; XS_NONE_D = 2'h2, XS_SOME_D = 2'h3 } xs_t; + + typedef enum logic [1:0] { + U_LEVEL = 2'h0, + S_LEVEL = 2'h1, + RESERVED_LEVEL = 2'h2, + M_LEVEL = 2'h3 + } priv_level_t; + + typedef enum logic [1:0] { + DIRECT = 2'h0, + VECTORED = 2'h1 + } vector_modes_t; + + typedef struct packed { + logic [29:0] base; + vector_modes_t mode; + } mtvec_t; typedef struct packed { logic sd; - logic [8:0] zero; - vm_t vm; + logic [7:0] reserved_3; + logic tsr; + logic tw; + logic tvm; + logic mxr; + logic sum; logic mprv; xs_t xs; - fs_t fs; - prv_lvl_t prv3; - logic ie3; - prv_lvl_t prv2; - logic ie2; - prv_lvl_t prv1; - logic ie1; - prv_lvl_t prv; - logic ie; + fs_t fs; + priv_level_t mpp; + logic [1:0] reserved_2; + logic spp; + logic mpie; + logic reserved_1; + logic spie; + logic upie; + logic mie; + logic reserved_0; + logic sie; + logic uie; } mstatus_t; /* mip and mie types */ - typedef struct packed { - logic [23:0] zero_2; + typedef struct packed { // total size for xlen (or mxlen) is 32 bits for our processor + logic [19:0] reserved_3; + logic meip; + logic reserved_2; + logic seip; + logic ueip; logic mtip; - logic htip; + logic reserved_1; logic stip; - logic zero_1; + logic utip; logic msip; - logic hsip; + logic reserved_0; logic ssip; - logic zero_0; + logic usip; } mip_t; typedef struct packed { - logic [23:0] zero_2; + logic [19:0] reserved_3; + logic meie; + logic reserved_2; + logic seie; + logic ueie; logic mtie; - logic htie; + logic reserved_1; logic stie; - logic zero_1; + logic utie; logic msie; - logic hsie; + logic reserved_0; logic ssie; - logic zero_0; + logic usie; } mie_t; /* mcause register variables */ @@ -204,24 +198,38 @@ package machine_mode_types_1_11_pkg; // ex_code_t should be cast from an // instantiation of mcause_t typedef enum logic [30:0] { - INSN_MAL = 31'h0, - INSN_FAULT = 31'h1, - ILLEGAL_INSN = 31'h2, - BREAKPOINT = 31'h3, - L_ADDR_MAL = 31'h4, - L_FAULT = 31'h5, - S_ADDR_MAL = 31'h6, - S_FAULT = 31'h7, - ENV_CALL_U = 31'h8, - ENV_CALL_S = 31'h9, - ENV_CALL_H = 31'ha, - ENV_CALL_M = 31'hb + INSN_MAL = 31'd0, + INSN_ACCESS = 31'd1, + ILLEGAL_INSN = 31'd2, + BREAKPOINT = 31'd3, + L_ADDR_MAL = 31'd4, + L_FAULT = 31'd5, + S_ADDR_MAL = 31'd6, + S_FAULT = 31'd7, + ENV_CALL_U = 31'd8, + ENV_CALL_S = 31'd9, + RESERVED_0 = 31'd10, + ENV_CALL_M = 31'd11, + INSN_PAGE = 31'd12, + LOAD_PAGE = 31'd13, + RESERVED_1 = 31'd14, + STORE_PAGE = 31'd15, + RESERVED_2 = 31'd16 } ex_code_t; typedef enum logic [30:0] { - SOFT_INT = 31'h0, - TIMER_INT = 31'h1, - EXT_INT = 31'hb //NON-STANDARD in priv-1.7 + SOFT_INT_U = 31'd0, + SOFT_INT_S = 31'd1, + RESERVED_4 = 31'd2, + SOFT_INT_M = 31'd3, + TIMER_INT_U = 31'd4, + TIMER_INT_S = 31'd5, + RESERVED_5 = 31'd6, + TIMER_INT_M = 31'd7, + EXT_INT_U = 31'd8, + EXT_INT_S = 31'd9, + RESERVED_6 = 31'd10, + EXT_INT_M = 31'd11 } int_code_t; /* Simple registers */ @@ -236,7 +244,7 @@ package machine_mode_types_1_11_pkg; typedef logic [31:0] mhartid_t; typedef logic [31:0] medeleg_t; typedef logic [31:0] mideleg_t; - typedef logic [31:0] mtvec_t; + //typedef logic [31:0] mtvec_t; typedef logic [31:0] mepc_t; typedef logic [31:0] mtime_t; typedef logic [31:0] mtimeh_t; diff --git a/source_code/privs/priv_1_11/priv_1_11_block.sv b/source_code/privs/priv_1_11/priv_1_11_block.sv index 2b6c9187c..6c005aeb4 100644 --- a/source_code/privs/priv_1_11/priv_1_11_block.sv +++ b/source_code/privs/priv_1_11/priv_1_11_block.sv @@ -24,32 +24,58 @@ `include "prv_pipeline_if.vh" `include "priv_1_11_internal_if.vh" +`include "core_interrupt_if.vh" module priv_1_11_block ( input logic CLK, nRST, - prv_pipeline_if.priv_block prv_pipe_if + prv_pipeline_if.priv_block prv_pipe_if, + core_interrupt_if.core interrupt_if + /*input logic plic_ext_int, + input logic clint_soft_int, + input logic clint_clear_soft_int, + input logic clint_timer_int, + input logic clint_clear_timer_int*/ ); + import machine_mode_types_1_11_pkg::*; + priv_1_11_internal_if prv_intern_if(); - logic [1:0] prv_intr, prv_ret; priv_1_11_csr_rfile csr_rfile_i(.*, .prv_intern_if(prv_intern_if)); priv_1_11_control prv_control_i(.*, .prv_intern_if(prv_intern_if)); priv_1_11_pipeline_control pipeline_control_i(.*, .prv_intern_if(prv_intern_if)); - //Machine Mode Only - assign prv_intr = 2'b11; - assign prv_ret = 2'b11; + // Disable interrupts that will not be used + assign prv_intern_if.timer_int_u = 1'b0; + assign prv_intern_if.timer_int_s = 1'b0; + assign prv_intern_if.timer_int_m = interrupt_if.timer_int; + assign prv_intern_if.soft_int_u = 1'b0; + assign prv_intern_if.soft_int_s = 1'b0; + assign prv_intern_if.soft_int_m = interrupt_if.soft_int; + assign prv_intern_if.ext_int_u = 1'b0; + assign prv_intern_if.ext_int_s = 1'b0; + assign prv_intern_if.ext_int_m = interrupt_if.ext_int; + assign prv_intern_if.reserved_0 = 1'b0; + assign prv_intern_if.reserved_1 = 1'b0; + assign prv_intern_if.reserved_2 = 1'b0; + + // Disable clear interrupts that will not be used + assign prv_intern_if.clear_timer_int_u = 1'b0; + assign prv_intern_if.clear_timer_int_s = 1'b0; + assign prv_intern_if.clear_timer_int_m = interrupt_if.timer_int_clear; + assign prv_intern_if.clear_soft_int_u = 1'b0; + assign prv_intern_if.clear_soft_int_s = 1'b0; + assign prv_intern_if.clear_soft_int_m = interrupt_if.soft_int_clear; + assign prv_intern_if.clear_ext_int_u = 1'b0; + assign prv_intern_if.clear_ext_int_s = 1'b0; + assign prv_intern_if.clear_ext_int_m = interrupt_if.ext_int_clear; - assign prv_intern_if.soft_int = 1'b0; - //TODO: PIC (Programmable Interrupt Controller) - assign prv_intern_if.ext_int = 1'b0; - // Assign inputs to the prv_block to the corresponding internal signals + // from pipeline to the priv unit assign prv_intern_if.pipe_clear = prv_pipe_if.pipe_clear; - assign prv_intern_if.ret = prv_pipe_if.ret; + assign prv_intern_if.mret = prv_pipe_if.ret; assign prv_intern_if.epc = prv_pipe_if.epc; - assign prv_intern_if.fault_insn = prv_pipe_if.fault_insn; + assign prv_intern_if.fault_insn_access = prv_pipe_if.fault_insn; assign prv_intern_if.mal_insn = prv_pipe_if.mal_insn; assign prv_intern_if.illegal_insn = prv_pipe_if.illegal_insn; assign prv_intern_if.fault_l = prv_pipe_if.fault_l; @@ -58,6 +84,11 @@ module priv_1_11_block ( assign prv_intern_if.mal_s = prv_pipe_if.mal_s; assign prv_intern_if.breakpoint = prv_pipe_if.breakpoint; assign prv_intern_if.env_m = prv_pipe_if.env_m; + assign prv_intern_if.env_s = 1'b0; + assign prv_intern_if.env_u = 1'b0; + assign prv_intern_if.fault_insn_page = 1'b0; + assign prv_intern_if.fault_load_page = 1'b0; + assign prv_intern_if.fault_store_page = 1'b0; assign prv_intern_if.mtval = prv_pipe_if.badaddr; assign prv_intern_if.swap = prv_pipe_if.swap; assign prv_intern_if.clr = prv_pipe_if.clr; @@ -70,7 +101,7 @@ module priv_1_11_block ( assign prv_intern_if.ex_rmgmt = prv_pipe_if.ex_rmgmt; assign prv_intern_if.ex_rmgmt_cause = prv_pipe_if.ex_rmgmt_cause; - // Assign outputs from internal signals to the outputs of the priv block + // from priv unit to pipeline assign prv_pipe_if.priv_pc = prv_intern_if.priv_pc; assign prv_pipe_if.insert_pc = prv_intern_if.insert_pc; assign prv_pipe_if.intr = prv_intern_if.intr; diff --git a/source_code/privs/priv_1_11/priv_1_11_control.sv b/source_code/privs/priv_1_11/priv_1_11_control.sv index 037d5c84a..002d3e84f 100644 --- a/source_code/privs/priv_1_11/priv_1_11_control.sv +++ b/source_code/privs/priv_1_11/priv_1_11_control.sv @@ -35,57 +35,105 @@ module priv_1_11_control ( logic exception; int_code_t intr_src; - logic interrupt; + logic interrupt, clear_interrupt; logic interrupt_reg, interrupt_fired; - always_comb begin + always_comb begin // determine the source of the interrupt to be stored in the mcause register interrupt = 1'b1; - intr_src = SOFT_INT; + intr_src = SOFT_INT_M; - if (prv_intern_if.timer_int) begin - intr_src = TIMER_INT; + if (prv_intern_if.ext_int_m) begin + intr_src = EXT_INT_M; + end + else if (prv_intern_if.soft_int_m) begin + intr_src = SOFT_INT_M; + end + else if (prv_intern_if.timer_int_m) begin + intr_src = TIMER_INT_M; + end + else if (prv_intern_if.ext_int_s) begin + intr_src = EXT_INT_S; + end + else if (prv_intern_if.soft_int_s) begin + intr_src = SOFT_INT_S; end - else if (prv_intern_if.soft_int) begin - intr_src = SOFT_INT; + else if (prv_intern_if.timer_int_s) begin + intr_src = TIMER_INT_S; end - else if (prv_intern_if.ext_int) begin - intr_src = EXT_INT; + else if (prv_intern_if.ext_int_u) begin + intr_src = EXT_INT_U; end + else if (prv_intern_if.soft_int_u) begin + intr_src = SOFT_INT_U; + end + else if (prv_intern_if.timer_int_u) begin + intr_src = TIMER_INT_U; + end + else interrupt = 1'b0; end - assign prv_intern_if.mip_rup = interrupt || prv_intern_if.clear_timer_int; - always_comb begin + assign clear_interrupt = (prv_intern_if.clear_timer_int_m || prv_intern_if.clear_soft_int_m || prv_intern_if.clear_ext_int_m || prv_intern_if.clear_timer_int_u || prv_intern_if.clear_soft_int_u || prv_intern_if.clear_ext_int_u || prv_intern_if.clear_timer_int_s || prv_intern_if.clear_soft_int_s || prv_intern_if.clear_ext_int_s); + + assign prv_intern_if.mip_rup = interrupt || clear_interrupt; + + always_comb begin // modify the pending status register prv_intern_if.mip_next = prv_intern_if.mip; - if (prv_intern_if.timer_int) prv_intern_if.mip_next.mtip = 1'b1; - if (prv_intern_if.clear_timer_int) prv_intern_if.mip_next.mtip = 1'b0; - if (prv_intern_if.soft_int) prv_intern_if.mip_next.msip = 1'b1; - if (prv_intern_if.ext_int) prv_intern_if.mip_next.msip = 1'b1; //external interrupts not specified in 1.7 + + if (prv_intern_if.ext_int_m) prv_intern_if.mip_next.meip = 1'b1; + else if (prv_intern_if.clear_ext_int_m) prv_intern_if.mip_next.meip = 1'b0; + else if (prv_intern_if.soft_int_m) prv_intern_if.mip_next.msip = 1'b1; + else if (prv_intern_if.clear_soft_int_m) prv_intern_if.mip_next.msip = 1'b0; + else if (prv_intern_if.timer_int_m) prv_intern_if.mip_next.mtip = 1'b1; + else if (prv_intern_if.clear_timer_int_m) prv_intern_if.mip_next.mtip = 1'b0; + else if (prv_intern_if.ext_int_s) prv_intern_if.mip_next.seip = 1'b1; + else if (prv_intern_if.clear_ext_int_s) prv_intern_if.mip_next.seip = 1'b0; + else if (prv_intern_if.soft_int_s) prv_intern_if.mip_next.ssip = 1'b1; + else if (prv_intern_if.clear_soft_int_s) prv_intern_if.mip_next.ssip = 1'b0; + else if (prv_intern_if.timer_int_s) prv_intern_if.mip_next.stip = 1'b1; + else if (prv_intern_if.clear_timer_int_s) prv_intern_if.mip_next.stip = 1'b0; + else if (prv_intern_if.ext_int_u) prv_intern_if.mip_next.ueip = 1'b1; + else if (prv_intern_if.clear_ext_int_u) prv_intern_if.mip_next.ueip = 1'b0; + else if (prv_intern_if.soft_int_u) prv_intern_if.mip_next.usip = 1'b1; + else if (prv_intern_if.clear_soft_int_u) prv_intern_if.mip_next.usip = 1'b0; + else if (prv_intern_if.timer_int_u) prv_intern_if.mip_next.utip = 1'b1; + else if (prv_intern_if.clear_timer_int_u) prv_intern_if.mip_next.utip = 1'b0; + end - always_comb begin + always_comb begin // determine whether or not an exception occured, as well as the source of the exception exception = 1'b1; ex_src = INSN_MAL; - if (prv_intern_if.fault_l) - ex_src = L_FAULT; - else if (prv_intern_if.mal_l) - ex_src = L_ADDR_MAL; - else if (prv_intern_if.fault_s) - ex_src = S_FAULT; - else if (prv_intern_if.mal_s) - ex_src = S_ADDR_MAL; - else if (prv_intern_if.breakpoint) + if (prv_intern_if.breakpoint) ex_src = BREAKPOINT; - else if (prv_intern_if.env_m) - ex_src = ENV_CALL_M; + else if (prv_intern_if.fault_insn_page) + ex_src = INSN_PAGE; + else if (prv_intern_if.fault_insn_access) + ex_src = INSN_ACCESS; else if (prv_intern_if.illegal_insn) ex_src = ILLEGAL_INSN; - else if (prv_intern_if.fault_insn) - ex_src = INSN_FAULT; else if (prv_intern_if.mal_insn) ex_src = INSN_MAL; + else if (prv_intern_if.env_u) + ex_src = ENV_CALL_U; + else if (prv_intern_if.env_s) + ex_src = ENV_CALL_S; + else if (prv_intern_if.env_m) + ex_src = ENV_CALL_M; + else if (prv_intern_if.mal_s) + ex_src = S_ADDR_MAL; + else if (prv_intern_if.mal_l) + ex_src = L_ADDR_MAL; + else if (prv_intern_if.fault_store_page) + ex_src = STORE_PAGE; + else if (prv_intern_if.fault_load_page) + ex_src = LOAD_PAGE; + else if (prv_intern_if.fault_s) + ex_src = S_FAULT; + else if (prv_intern_if.fault_l) + ex_src = L_FAULT; else if (prv_intern_if.ex_rmgmt) ex_src = ex_code_t'(prv_intern_if.ex_rmgmt_cause); else @@ -94,34 +142,38 @@ module priv_1_11_control ( //output to pipeline control assign prv_intern_if.intr = exception | interrupt_reg; - assign interrupt_fired = (prv_intern_if.mstatus.ie & ((prv_intern_if.mie.mtie & prv_intern_if.mip.mtip) | - (prv_intern_if.mie.msie & prv_intern_if.mip.msip))); + assign interrupt_fired = (prv_intern_if.mstatus.mie & ((prv_intern_if.mie.mtie & prv_intern_if.mip.mtip) | + (prv_intern_if.mie.msie & prv_intern_if.mip.msip) | (prv_intern_if.mie.meie & prv_intern_if.mip.meip))); // Register Updates on Interrupt/Exception - assign prv_intern_if.mcause_rup = exception | interrupt_fired; + assign prv_intern_if.mcause_rup = exception | interrupt; // TODO: Change to interrupt assign prv_intern_if.mcause_next.interrupt = ~exception; assign prv_intern_if.mcause_next.cause = exception ? ex_src : intr_src; - assign prv_intern_if.mstatus_rup = exception | interrupt_fired; + assign prv_intern_if.mstatus_rup = exception | prv_intern_if.intr | prv_intern_if.mret; // TODO: Change to prv_intern_if.intr always_comb begin - if (prv_intern_if.intr) begin - prv_intern_if.mstatus_next.ie = 1'b0; - end else if (prv_intern_if.ret) begin - prv_intern_if.mstatus_next.ie = 1'b1; - end - else begin - prv_intern_if.mstatus_next.ie = prv_intern_if.mstatus.ie; + prv_intern_if.mstatus_next.mie = prv_intern_if.mstatus.mie; + prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mpie; + + if (prv_intern_if.intr) begin // interrupt has truly been registered and it is time to go to the vector table + prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mie; // when a trap is taken mpie is set to the current mie + prv_intern_if.mstatus_next.mie = 1'b0; // disable the interrupt once it enters the handler + + end else if (prv_intern_if.mret) begin // leaving the vector table + prv_intern_if.mstatus_next.mpie = 1'b1; + prv_intern_if.mstatus_next.mie = prv_intern_if.mstatus.mpie; end end + // Update EPC as soon as interrupt or exception is found - assign prv_intern_if.mepc_rup = exception | interrupt_fired; + assign prv_intern_if.mepc_rup = exception | interrupt; // TODO: Change to interrupt assign prv_intern_if.mepc_next = prv_intern_if.epc; assign prv_intern_if.mtval_rup = (prv_intern_if.mal_l | prv_intern_if.fault_l | prv_intern_if.mal_s | prv_intern_if.fault_s | - prv_intern_if.illegal_insn | prv_intern_if.fault_insn | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) - & prv_intern_if.pipe_clear; + prv_intern_if.illegal_insn | prv_intern_if.fault_insn_access | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) + & prv_intern_if.pipe_clear; // TODO: May need to insert other exception signals assign prv_intern_if.mtval_next = prv_intern_if.mtval; /* Interrupt needs to be latched until pipeline cleared */ @@ -136,4 +188,5 @@ module priv_1_11_control ( else if (prv_intern_if.pipe_clear) interrupt_reg <= '0; end + endmodule diff --git a/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv b/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv index 17316b08f..0a8a52fcc 100644 --- a/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv +++ b/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv @@ -39,21 +39,20 @@ module priv_1_11_csr_rfile ( marchid_t marchid; mimpid_t mimpid; mhartid_t mhartid; - misaid_t misaid; + misaid_t misaid, misaid_next, misaid_temp, misaid_default; - assign misaid.base = BASE_RV32; - assign misaid.zero = '0; - assign misaid.extensions = MISAID_EXT_I `ifdef RV32M_SUPPORTED | - MISAID_EXT_M `endif `ifdef RV32C_SUPPORTED | - MISAID_EXT_C `endif `ifdef RV32F_SUPPORTED | - MISAID_EXT_F `endif `ifdef CUSTOM_SUPPORTED | - MISAID_EXT_X `endif; + assign misaid_default.base = BASE_RV32; + assign misaid_default.zero = '0; + assign misaid_default.extensions = MISAID_EXT_I `ifdef RV32M_SUPPORTED | + MISAID_EXT_M `endif `ifdef RV32C_SUPPORTED | + MISAID_EXT_C `endif `ifdef RV32F_SUPPORTED | + MISAID_EXT_F `endif `ifdef CUSTOM_SUPPORTED | + MISAID_EXT_X `endif; //TODO: Version Numbering Convention assign mvendorid = '0; assign marchid = '0; assign mimpid = '0; - assign mhartid = '0; @@ -65,37 +64,49 @@ module priv_1_11_csr_rfile ( mie_t mie, mie_next; mtvec_t mtvec, mtvec_next; - assign mstatus.zero = '0; - - // mstatus bits set due to only Machine Mode Implemented - assign mstatus.prv = M_MODE; - assign mstatus.prv1 = M_MODE; - assign mstatus.ie1 = 1'b0; - assign mstatus.prv2 = M_MODE; - assign mstatus.ie2 = 1'b0; - assign mstatus.prv3 = M_MODE; - assign mstatus.ie3 = 1'b0; + // Privilege and Global Interrupt-Enable Stack + assign mstatus.uie = 1'b0; + assign mstatus.sie = 1'b0; + assign mstatus.reserved_0 = 1'b0; + assign mstatus.upie = 1'b0; + assign mstatus.spie = 1'b0; + assign mstatus.reserved_1 = 1'b0; + assign mstatus.spp = 1'b0; + assign mstatus.reserved_2 = 2'b0; + assign mstatus.mpp = M_LEVEL; // No memory protection - assign mstatus.vm = VM_MBARE; assign mstatus.mprv = 1'b0; + assign mstatus.sum = 1'b0; + assign mstatus.mxr = 1'b0; + + // No virtualization protection + assign mstatus.tvm = 1'b0; + assign mstatus.tw = 1'b0; + assign mstatus.tsr = 1'b0; // No FPU or Extensions assign mstatus.xs = XS_ALL_OFF; - assign mstatus.fs = FS_OFF; - assign mstatus.sd = 1'b0; + assign mstatus.fs = FS_OFF; // Even though FPU will be integrated for AFTx06, there is no functionality for Supervisor Mode + assign mstatus.sd = (mstatus.fs == FS_DIRTY) | (mstatus.xs == XS_SOME_D); + assign mstatus.reserved_3 = '0; + + // Deleg Register Zero in Machine Mode Only (Should be removed) assign medeleg = '0; assign mideleg = '0; - assign mie.zero_0 = '0; - assign mie.zero_1 = '0; - assign mie.zero_2 = '0; - assign mie.htie = 1'b0; + assign mie.reserved_0 = '0; + assign mie.reserved_1 = '0; + assign mie.reserved_2 = '0; + assign mie.reserved_3 = '0; + assign mie.utie = 1'b0; assign mie.stie = 1'b0; - assign mie.hsie = 1'b0; + assign mie.usie = 1'b0; assign mie.ssie = 1'b0; + assign mie.ueie = 1'b0; + assign mie.seie = 1'b0; /* Machine Trap Handling */ @@ -105,30 +116,16 @@ module priv_1_11_csr_rfile ( mtval_t mtval, mtval_next; mip_t mip, mip_next; - assign mip.zero_0 = '0; - assign mip.zero_1 = '0; - assign mip.zero_2 = '0; - assign mip.htip = 1'b0; + assign mip.reserved_0 = '0; + assign mip.reserved_1 = '0; + assign mip.reserved_2 = '0; + assign mip.reserved_3 = '0; + assign mip.utip = 1'b0; assign mip.stip = 1'b0; - assign mip.hsip = 1'b0; + assign mip.usip = 1'b0; assign mip.ssip = 1'b0; - - - /* Machine Protection and Translation */ - // Unimplemented, only MBARE supported - - - /* Machine Timers and Counters */ - mtimecmp_t mtimecmp, mtimecmp_next; - mtime_t mtime, mtime_next; - mtimeh_t mtimeh, mtimeh_next; - logic [63:0] mtimefull, mtimefull_next; - assign mtime = mtimefull[31:0]; - assign mtimeh = mtimefull[63:32]; - assign mtimefull_next = mtimefull + 1; - assign prv_intern_if.timer_int = (mtime == mtimecmp); - assign prv_intern_if.clear_timer_int = (prv_intern_if.addr == MTIMECMP_ADDR) & - prv_intern_if.valid_write; + assign mip.ueip = 1'b0; + assign mip.seip = 1'b0; /* Machine Counter Delta Registers */ // Unimplemented, only Machine Mode Supported @@ -151,85 +148,40 @@ module priv_1_11_csr_rfile ( assign instretfull_next = (prv_intern_if.instr_retired == 1'b1) ? instretfull + 1 : instretfull; - //Non Standard Extensions, used for testing - mtohost_t mtohost, mtohost_next; - mfromhost_t mfromhost, mfromhost_next; - always_ff @ (posedge CLK, negedge nRST) begin if (~nRST) begin - mstatus.ie <= 1'b1; + mstatus.mie <= 1'b0; + mstatus.mpie <= 1'b0; mie.mtie <= 1'b0; mie.msie <= 1'b0; mip.msip <= 1'b0; mip.mtip <= 1'b0; + mie.meie <= 1'b0; + mip.meip <= 1'b0; + misaid <= misaid_default; mtvec <= '0; mcause <= '0; mepc <= '0; mtval <= '0; - mscratch <= '0; - mtohost <= '0; - mfromhost <= '0; - mtimecmp <= '0; - mtimefull <= '0; - /* Performance Counters */ timefull <= '0; cyclefull <= '0; instretfull <= '0; - end else if (prv_intern_if.addr == MTIMEH_ADDR)begin - mstatus.ie <= mstatus_next.ie; - mie.mtie <= mie_next.mtie; - mie.msie <= mie_next.msie; - mip.msip <= mip_next.msip; // interrupt - mip.mtip <= mip_next.mtip; // interrupt - mtvec <= mtvec_next; - mcause <= mcause_next; - mepc <= mepc_next; - mtval <= mtval_next; - mscratch <= mscratch_next; - mtohost <= mtohost_next; - mfromhost <= mfromhost_next; - mtimecmp <= mtimecmp_next; - mtimefull <= {mtimeh_next, mtimefull_next[31:0]}; - /* Performance Counters */ - timefull <= timefull_next; - cyclefull <= cyclefull_next; - instretfull <= instretfull_next; - end else if (prv_intern_if.addr == MTIME_ADDR) begin - mstatus.ie <= mstatus_next.ie; - mie.mtie <= mie_next.mtie; - mie.msie <= mie_next.msie; - mip.msip <= mip_next.msip; // interrupt - mip.mtip <= mip_next.mtip; // interrupt - mtvec <= mtvec_next; - mcause <= mcause_next; - mepc <= mepc_next; - mtval <= mtval_next; - mscratch <= mscratch_next; - mtohost <= mtohost_next; - mfromhost <= mfromhost_next; - mtimecmp <= mtimecmp_next; - mtimefull <= {mtimefull_next[63:32], mtime_next}; - /* Performance Counters */ - timefull <= timefull_next; - cyclefull <= cyclefull_next; - instretfull <= instretfull_next; end else begin - mstatus.ie <= mstatus_next.ie; - mie.mtie <= mie_next.mtie; + mstatus.mie <= mstatus_next.mie; + mstatus.mpie <= mstatus_next.mpie; + mie.mtie <= mie_next.mtie; mie.msie <= mie_next.msie; - mip.msip <= mip_next.msip; // interrupt - mip.mtip <= mip_next.mtip; // interrupt + mie.meie <= mie_next.meie; + mip.msip <= mip_next.msip; + mip.mtip <= mip_next.mtip; + mip.meip <= mip_next.meip; + misaid <= misaid_next; mtvec <= mtvec_next; mcause <= mcause_next; mepc <= mepc_next; mtval <= mtval_next; mscratch <= mscratch_next; - mtohost <= mtohost_next; - mfromhost <= mfromhost_next; - mtimecmp <= mtimecmp_next; - mtimefull <= mtimefull_next; - /* Performance Counters */ timefull <= timefull_next; cyclefull <= cyclefull_next; instretfull <= instretfull_next; @@ -257,33 +209,39 @@ module priv_1_11_csr_rfile ( prv_intern_if.rdata ); - // Readonly by pipeline, rw by prv + // Readonly by pipeline, rw by prv, controlled by hardware assign mip_next = prv_intern_if.mip_rup ? prv_intern_if.mip_next : mip; assign mtval_next = prv_intern_if.mtval_rup ? prv_intern_if.mtval_next : mtval; assign mcause_next = prv_intern_if.mcause_rup ? prv_intern_if.mcause_next : mcause; // Read and write by pipeline and prv - //TODO: Waveforms for this look wrong, potential bug assign mstatus_next = (prv_intern_if.addr == MSTATUS_ADDR) ? mstatus_t'(rup_data) : ( prv_intern_if.mstatus_rup ? prv_intern_if.mstatus_next : mstatus ); - assign mepc_next = (prv_intern_if.addr == MEPC_ADDR) ? mepc_t'(rup_data) : ( prv_intern_if.mepc_rup ? prv_intern_if.mepc_next : mepc ); - // Read and write by pipeline + + // Readonly by priv, rw by pipeline, assigned based on csr instructions assign mie_next = (prv_intern_if.addr == MIE_ADDR) ? mie_t'(rup_data) : mie; assign mtvec_next = (prv_intern_if.addr == MTVEC_ADDR) ? mtvec_t'(rup_data) : mtvec; assign mscratch_next = (prv_intern_if.addr == MSCRATCH_ADDR) ? mscratch_t'(rup_data) : mscratch; - assign mtohost_next = (prv_intern_if.addr == MTOHOST_ADDR) ? mtohost_t'(rup_data) : mtohost; - assign mtime_next = (prv_intern_if.addr == MTIME_ADDR) ? mtime_t'(rup_data) : mtime; - assign mtimeh_next = (prv_intern_if.addr == MTIMEH_ADDR) ? mtimeh_t'(rup_data) : mtimeh; - assign mtimecmp_next = (prv_intern_if.addr == MTIMECMP_ADDR) ? mtimecmp_t'(rup_data) : mtimecmp; - + // Ensure legal MISA value - WARL always_comb begin + misaid_temp = misaid_t'(rup_data); + if(prv_intern_if.addr == MISA_ADDR && misaid_temp.base != 2'b00 + && (misaid_temp.extensions & MISAID_EXT_E) ^ (misaid_temp.extensions & MISAID_EXT_I) != 'b1 + && misaid_temp.zero == 4'b0) begin + misaid_next = misaid_temp; + end else begin + misaid_next = misaid; + end + end + + always_comb begin // register to send to pipeline based on the address valid_csr_addr = 1'b1; casez (prv_intern_if.addr) MVENDORID_ADDR : prv_intern_if.rdata = mvendorid; @@ -304,33 +262,10 @@ module priv_1_11_csr_rfile ( MTVAL_ADDR : prv_intern_if.rdata = mtval; MIP_ADDR : prv_intern_if.rdata = mip; - //machine protection and translation not present - MBASE_ADDR : prv_intern_if.rdata = '0; - MBOUND_ADDR : prv_intern_if.rdata = '0; - MIBASE_ADDR : prv_intern_if.rdata = '0; - MIBOUND_ADDR : prv_intern_if.rdata = '0; - MDBASE_ADDR : prv_intern_if.rdata = '0; - MDBOUND_ADDR : prv_intern_if.rdata = '0; - - //only machine mode - HTIMEW_ADDR : prv_intern_if.rdata = '0; - HTIMEHW_ADDR : prv_intern_if.rdata = '0; - - //Timers - MTIMECMP_ADDR : prv_intern_if.rdata = mtimecmp; - MTIME_ADDR : prv_intern_if.rdata = mtime; - MTIMEH_ADDR : prv_intern_if.rdata = mtimeh; - - // Non-Standard mtohost/mfromhost - MTOHOST_ADDR : prv_intern_if.rdata = mtohost; - MFROMHOST_ADDR : prv_intern_if.rdata = mfromhost; - // Performance counters MCYCLE_ADDR : prv_intern_if.rdata = cycle; - MTIME_ADDR : prv_intern_if.rdata = _time; MINSTRET_ADDR : prv_intern_if.rdata = instret; MCYCLEH_ADDR : prv_intern_if.rdata = cycleh; - MTIMEH_ADDR : prv_intern_if.rdata = timeh; MINSTRETH_ADDR : prv_intern_if.rdata = instreth; default : begin @@ -347,7 +282,5 @@ module priv_1_11_csr_rfile ( assign prv_intern_if.mcause = mcause; assign prv_intern_if.mip = mip; - assign prv_intern_if.xtvec[2'b11] = mtvec; - assign prv_intern_if.xepc_r[2'b11] = mepc; endmodule diff --git a/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv b/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv index cb727adb9..45a671fb3 100644 --- a/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv +++ b/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv @@ -22,36 +22,32 @@ * Description: Control signals for the pipeline from the exception/IRQ * block */ - +// Code will mainly be used as pipeline control `include "priv_1_11_internal_if.vh" module priv_1_11_pipeline_control ( - input logic [1:0] prv_intr, prv_ret, - priv_1_11_internal_if.pipe_ctrl prv_intern_if + priv_1_11_internal_if.pipe_ctrl prv_intern_if // interface for pipeline control ); + import machine_mode_types_1_11_pkg::*; import rv32i_types_pkg::*; - logic interrupt_pending; - - assign prv_intern_if.insert_pc = prv_intern_if.ret | (prv_intern_if.pipe_clear & prv_intern_if.intr); + assign prv_intern_if.insert_pc = prv_intern_if.mret | (prv_intern_if.pipe_clear & prv_intern_if.intr); // insert the PC + + always_comb begin - if(prv_intern_if.intr) - case(prv_intr) - 2'b00: prv_intern_if.priv_pc = prv_intern_if.xtvec[2'b00]; - 2'b01: prv_intern_if.priv_pc = prv_intern_if.xtvec[2'b01]; - 2'b10: prv_intern_if.priv_pc = prv_intern_if.xtvec[2'b10]; - 2'b11: prv_intern_if.priv_pc = prv_intern_if.xtvec[2'b11]; - endcase - else if (prv_intern_if.ret) - case(prv_ret) - 2'b00: prv_intern_if.priv_pc = prv_intern_if.xepc_r[2'b00]; - 2'b01: prv_intern_if.priv_pc = prv_intern_if.xepc_r[2'b01]; - 2'b10: prv_intern_if.priv_pc = prv_intern_if.xepc_r[2'b10]; - 2'b11: prv_intern_if.priv_pc = prv_intern_if.xepc_r[2'b11]; - endcase - else - prv_intern_if.priv_pc = 32'b0; - end + prv_intern_if.priv_pc = 'Z; + + if(prv_intern_if.intr) begin + if (prv_intern_if.mtvec.mode == VECTORED & prv_intern_if.mcause.interrupt) begin // vectored mode based on the interrupt source + prv_intern_if.priv_pc = (prv_intern_if.mtvec.base << 2) + (prv_intern_if.mcause.cause << 2); + end else + prv_intern_if.priv_pc = prv_intern_if.mtvec.base << 2; + + end else if (prv_intern_if.mret) + prv_intern_if.priv_pc = prv_intern_if.mepc; // when leaving the ISR, restore to the original PC + + end + endmodule diff --git a/source_code/privs/priv_wrapper.sv b/source_code/privs/priv_wrapper.sv index 66f114825..7eed20e5e 100644 --- a/source_code/privs/priv_wrapper.sv +++ b/source_code/privs/priv_wrapper.sv @@ -23,10 +23,12 @@ */ `include "prv_pipeline_if.vh" +`include "core_interrupt_if.vh" module priv_wrapper ( input logic CLK, nRST, - prv_pipeline_if.priv_block prv_pipe_if + prv_pipeline_if.priv_block prv_pipe_if, + core_interrupt_if.core interrupt_if ); //TODO: Select different priv blocks for backwards compatibility diff --git a/source_code/privs/wscript b/source_code/privs/wscript index 91a6677cb..99e992732 100644 --- a/source_code/privs/wscript +++ b/source_code/privs/wscript @@ -2,7 +2,7 @@ #encoding: utf-8 def configure(cnf): - cnf.recurse(priv_1_7) + cnf.recurse(priv_1_11) def sim_source(cnf): - cnf.recurse(priv_1_7) + cnf.recurse(priv_1_11) diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index 817878f73..3e9528e5e 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -30,9 +30,11 @@ `include "sparce_pipeline_if.vh" `include "tspp_fetch_execute_if.vh" `include "tspp_hazard_unit_if.vh" +`include "core_interrupt_if.vh" module RISCVBusiness ( input logic CLK, nRST, + core_interrupt_if.core interrupt_if, `ifdef BUS_INTERFACE_GENERIC_BUS generic_bus_if.cpu gen_bus_if `elsif BUS_INTERFACE_AHB @@ -67,7 +69,7 @@ module RISCVBusiness ( .halt(halt), .igen_bus_if(tspp_icache_gen_bus_if), .dgen_bus_if(tspp_dcache_gen_bus_if), - .prv_pipe_if(prv_pipe_if), + .prv_pipe_if(prv_pipe_if), // TODO: Look at the communications between pipeline_wrapper and priv_wrapper .predict_if(predict_if), .rm_if(rm_if), .cc_if(cc_if), @@ -115,7 +117,8 @@ module RISCVBusiness ( priv_wrapper priv_wrapper_i ( .CLK(CLK), .nRST(nRST), - .prv_pipe_if(prv_pipe_if) + .prv_pipe_if(prv_pipe_if), + .interrupt_if ); risc_mgmt_wrapper rmgmt ( diff --git a/source_code/tb/tb_RISCVBusiness.sv b/source_code/tb/tb_RISCVBusiness.sv index cc44d838a..42f070513 100644 --- a/source_code/tb/tb_RISCVBusiness.sv +++ b/source_code/tb/tb_RISCVBusiness.sv @@ -49,14 +49,22 @@ module tb_RISCVBusiness (); generic_bus_if gen_bus_if(); generic_bus_if rvb_gen_bus_if(); generic_bus_if tb_gen_bus_if(); + core_interrupt_if interrupt_if(); + + assign interrupt_if.timer_int = '0; + assign interrupt_if.timer_int_clear = '0; + assign interrupt_if.ext_int = '0; + assign interrupt_if.ext_int_clear = '0; + assign interrupt_if.soft_int = '0; + assign interrupt_if.soft_int_clear = '0; //Module Instantiations RISCVBusiness DUT ( .CLK(CLK), .nRST(nRST), - //.halt(halt), - .gen_bus_if(rvb_gen_bus_if) + .gen_bus_if(rvb_gen_bus_if), + .interrupt_if ); ram_wrapper ram ( diff --git a/source_code/tb/tb_RISCVBusiness_self_test.sv b/source_code/tb/tb_RISCVBusiness_self_test.sv index 655fa3576..5311b6524 100644 --- a/source_code/tb/tb_RISCVBusiness_self_test.sv +++ b/source_code/tb/tb_RISCVBusiness_self_test.sv @@ -50,13 +50,22 @@ module tb_RISCVBusiness_self_test (); generic_bus_if gen_bus_if(); generic_bus_if rvb_gen_bus_if(); generic_bus_if tb_gen_bus_if(); + core_interrupt_if interrupt_if(); + + assign interrupt_if.timer_int = '0; + assign interrupt_if.timer_int_clear = '0; + assign interrupt_if.ext_int = '0; + assign interrupt_if.ext_int_clear = '0; + assign interrupt_if.soft_int = '0; + assign interrupt_if.soft_int_clear = '0; //Module Instantiations RISCVBusiness DUT ( .CLK(CLK), .nRST(nRST), - .gen_bus_if(rvb_gen_bus_if) + .gen_bus_if(rvb_gen_bus_if), + .interrupt_if ); ram_wrapper ram ( diff --git a/source_code/tb/tb_RISCVBusiness_self_test_plic.sv b/source_code/tb/tb_RISCVBusiness_self_test_plic.sv new file mode 100644 index 000000000..34934ce5e --- /dev/null +++ b/source_code/tb/tb_RISCVBusiness_self_test_plic.sv @@ -0,0 +1,278 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_RISCVBusiness_self_test.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Testbench for running RISCVBusiness until a halt condition. +* The test bench monitors memory location 0x1000 and prints +* the char representation of what is written. +*/ + +`timescale 1ns/100ps + +`include "generic_bus_if.vh" +`include "component_selection_defines.vh" +`include "core_interrupt_if.vh" + +`define OUTPUT_FILE_NAME "cpu.hex" +`define STATS_FILE_NAME "stats.txt" +`define RVBSELF_CLK_TIMEOUT 5000000 + +module tb_RISCVBusiness_self_test_plic (); + + parameter PERIOD = 20; + + logic CLK, nRST; + logic ram_control; // 1 -> CORE, 0 -> TB + logic halt; + logic [31:0] addr, data, data_temp; + logic [63:0] hexdump_temp; + logic [7:0] checksum; + integer fptr, stats_ptr; + integer clk_count; + logic plic_ext_int; //plic_clear_ext_int_m; + + //Interface Instantiations + generic_bus_if gen_bus_if(); + generic_bus_if rvb_gen_bus_if(); + generic_bus_if tb_gen_bus_if(); + core_interrupt_if interrupt_if(); + + // additional instantiation for the priv unit to control the external interrupt signal + + //Module Instantiations + + RISCVBusiness DUT ( + .CLK(CLK), + .nRST(nRST), + //.halt(halt), + //.ahb_master(ahb_bus_if), + .gen_bus_if(rvb_gen_bus_if), + .interrupt_if + //.plic_ext_int(plic_ext_int) + //plic_clear_ext_int_m(//plic_clear_ext_int_m) + ); + + ram_wrapper ram ( + .CLK(CLK), + .nRST(nRST), + .gen_bus_if(gen_bus_if) + ); + + if (BUS_ENDIANNESS == "big") + endian_swapper swap(data_temp, data); + else if (BUS_ENDIANNESS == "little") + assign data = data_temp; + else ;//TODO:ERROR + + bind tspp_execute_stage cpu_tracker cpu_track1 ( + .CLK(CLK), + .wb_stall(wb_stall), + .instr(fetch_ex_if.fetch_ex_reg.instr), + .pc(fetch_ex_if.fetch_ex_reg.pc), + .opcode(cu_if.opcode), + .funct3(funct3), + .funct12(funct12), + .rs1(rf_if.rs1), + .rs2(rf_if.rs2), + .rd(rf_if.rd), + .imm_S(cu_if.imm_S), + .imm_I(cu_if.imm_I), + .imm_U(cu_if.imm_U), + .imm_UJ(imm_UJ_ext), + .imm_SB(cu_if.imm_SB), + .instr_30(instr_30) + ); + + bind branch_predictor_wrapper branch_tracker branch_perf( + .CLK(CLK), + .nRST(nRST), + .update_predictor(predict_if.update_predictor), + .prediction(predict_if.prediction), + .branch_result(predict_if.branch_result) + ); + + //Ramif Mux + always_comb begin + if(ram_control) begin + /* No actual bus, so directly connect ram to generic bus interface */ + gen_bus_if.addr = rvb_gen_bus_if.addr; + gen_bus_if.ren = rvb_gen_bus_if.ren; + gen_bus_if.wen = rvb_gen_bus_if.wen; + gen_bus_if.wdata = rvb_gen_bus_if.wdata; + gen_bus_if.byte_en = rvb_gen_bus_if.byte_en; //ahb_bus_if.byte_en; + end else begin + gen_bus_if.addr = tb_gen_bus_if.addr; + gen_bus_if.ren = tb_gen_bus_if.ren; + gen_bus_if.wen = tb_gen_bus_if.wen; + gen_bus_if.wdata = tb_gen_bus_if.wdata; + gen_bus_if.byte_en = tb_gen_bus_if.byte_en; + end + end + + /* No actual bus, so directly connect ram to generic bus interface */ + assign rvb_gen_bus_if.rdata = gen_bus_if.rdata; + assign rvb_gen_bus_if.busy = gen_bus_if.busy; + assign tb_gen_bus_if.rdata = gen_bus_if.rdata; + assign tb_gen_bus_if.busy = gen_bus_if.busy; + + + //Clock generation + initial begin : INIT + CLK = 0; + end : INIT + + always begin : CLOCK_GEN + #(PERIOD/2) CLK = ~CLK; + end : CLOCK_GEN + + //Setup core and let it run + initial begin : CORE_RUN + nRST = 0; + ram_control = 1; + clk_count = 0; + + @(posedge CLK); + @(posedge CLK); + + nRST = 1; + interrupt_if.ext_int = 1'b0; + interrupt_if.soft_int = '0; + interrupt_if.timer_int = '0; + interrupt_if.ext_int_clear = '0; + interrupt_if.soft_int_clear = '0; + interrupt_if.timer_int_clear = '0; + + // Give start time, then trigger external interrupt + #(PERIOD * 200); + interrupt_if.ext_int = 1'b1; + #(PERIOD); + interrupt_if.ext_int = 1'b0; + #(PERIOD * 2); + interrupt_if.ext_int_clear = 1'b1; + #(PERIOD); + interrupt_if.ext_int_clear = 1'b0; + + // Trigger software interrupt + #(PERIOD * 20); + interrupt_if.soft_int = 1'b1; + #(PERIOD); + interrupt_if.soft_int = 1'b0; + #(PERIOD * 2); + interrupt_if.soft_int_clear = 1'b1; + #(PERIOD); + interrupt_if.soft_int_clear = 1'b0; + + // Trigger timer interrupt + #(PERIOD * 20); + interrupt_if.timer_int = 1'b1; + #(PERIOD); + interrupt_if.timer_int = 1'b0; + #(PERIOD * 2); + interrupt_if.timer_int_clear = 1'b1; + #(PERIOD); + interrupt_if.timer_int_clear = 1'b0; + + while (DUT.halt == 0 && clk_count != `RVBSELF_CLK_TIMEOUT) begin + @(posedge CLK); + clk_count++; + if(gen_bus_if.addr == 16'h0000 & !gen_bus_if.busy & gen_bus_if.wen) begin + $write("%c", gen_bus_if.wdata[31:24]); + end + // TODO: Check clock count + end + + #(1); + + dump_stats(); + dump_ram(); + + // Check Register 28 to see if test passed or failed + if (clk_count == `RVBSELF_CLK_TIMEOUT) + $display("ERROR: Test timed out"); + else if(DUT.execute_stage_i.rf.registers[28] != 32'h1) + $display("ERROR: Test %0d did not pass", + (DUT.execute_stage_i.rf.registers[28] - 1)/2); + else + $display("SUCCESS"); + $finish; + + end : CORE_RUN + + task dump_stats(); + logic [63:0] instret, cycles; + instret = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.instretfull; + cycles = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.cyclefull; + if (cycles != clk_count) $info("Cycles CSR (%0d) != clk_count (%0d)", cycles, clk_count); + stats_ptr = $fopen(`STATS_FILE_NAME, "w"); + $fwrite(stats_ptr, "Instructions retired: %2d\n", instret); + $fwrite(stats_ptr, "Cycles taken: %2d\n", cycles); + $fwrite(stats_ptr, "CPI: %5f\n", real'(cycles)/instret); + $fwrite(stats_ptr, "IPC: %5f\n", real'(instret)/cycles); + $fclose(stats_ptr); + endtask + + task dump_ram (); + ram_control = 0; + tb_gen_bus_if.addr = 0; + tb_gen_bus_if.ren = 0; + tb_gen_bus_if.wen = 0; + tb_gen_bus_if.wdata = 0; + tb_gen_bus_if.byte_en = 4'hf; + + fptr = $fopen(`OUTPUT_FILE_NAME, "w"); + + for(addr = 32'h100; addr < 32'h2000; addr+=4) begin + read_ram(addr, data_temp); + #(PERIOD/4); + hexdump_temp = {8'h04, addr[15:0]>>2, 8'h00, data}; + checksum = calculate_crc(hexdump_temp); + if(data != 0) + $fwrite(fptr, ":%2h%4h00%8h%2h\n", 8'h4, addr[15:0]>>2, data, checksum); + end + // add the EOL entry to the file + $fwrite(fptr, ":00000001FF"); + + endtask + + task read_ram (input logic [31:0] raddr, output logic [31:0] rdata); + @(posedge CLK); + tb_gen_bus_if.addr = raddr; + tb_gen_bus_if.ren = 1; + @(posedge CLK); + while(tb_gen_bus_if.busy == 1) @(posedge CLK); + rdata = tb_gen_bus_if.rdata; + tb_gen_bus_if.ren = 0; + endtask + + function [7:0] calculate_crc (logic [63:0] hex_line); + static logic [7:0] checksum = 0; + int i; + + checksum = hex_line[7:0] + hex_line[15:8] + hex_line[23:16] + + hex_line[31:24] + hex_line[39:32] + hex_line[47:40] + + hex_line[55:48] + hex_line[63:56]; + + //take two's complement + checksum = (~checksum) + 1; + return checksum; + endfunction + +endmodule + diff --git a/source_code/tb/tb_priv_1_11_block.sv b/source_code/tb/tb_priv_1_11_block.sv new file mode 100644 index 000000000..cf8641d0b --- /dev/null +++ b/source_code/tb/tb_priv_1_11_block.sv @@ -0,0 +1,201 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_RISCVBusiness.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Testbench for running RISCVBusiness until a halt condition. +* A hexdump of memory will occur after the halt condition. +*/ + +// Note: Figure out whether or not I need to change the name to Enes Shaltami. + +`timescale 1ns/100ps + +`include "prv_pipeline_if.vh" +`include "priv_1_11_internal_if.vh" + +`define OUTPUT_FILE_NAME "cpu.hex" +`define STATS_FILE_NAME "stats.txt" +`define RVB_CLK_TIMEOUT 10000 + +module tb_priv_1_11_block (); + + parameter PERIOD = 20; + + logic CLK, nRST; + logic ram_control; // 1 -> CORE, 0 -> TB + logic halt; + logic [31:0] addr, data_temp, data; + logic [63:0] hexdump_temp; + logic [7:0] checksum; + integer fptr, stats_ptr; + //integer clk_count; + + //Interface Instantiations + prv_pipeline_if prv_pipeline_if(); + + //Module Instantiations + + priv_1_11_block DUT ( + .CLK(CLK), + .nRST(nRST), + .prv_pipe_if(prv_pipeline_if) // using the "priv_block" modport of the prv_pieline_if.vh file + ); // TODO: Figure out IO for the priv pipeline unit + + //Clock generation + initial begin : INIT + CLK = 0; + end : INIT + + always begin : CLOCK_GEN + #(PERIOD/2) CLK = ~CLK; + end : CLOCK_GEN + + //Setup core and let it run + initial begin : CORE_RUN + nRST = 0; + + // TODO: Expected output signals: ex_src, exception, + + +/* + // Resetting all of the inputs for the priv_block unit + prv_pipeline_if.pipe_clear = '0; + prv_pipeline_if.ret = '0; + prv_pipeline_if.epc = '0; + + // control signals for the exception combinational logic + prv_pipeline_if.fault_insn = 1'b0; + prv_pipeline_if.mal_insn = 1'b0; + prv_pipeline_if.illegal_insn = 1'b0; + prv_pipeline_if.fault_l = 1'b0; + prv_pipeline_if.mal_l = 1'b0; + prv_pipeline_if.fault_s = 1'b0; + prv_pipeline_if.mal_s = 1'b0; + prv_pipeline_if.breakpoint = 1'b0; + prv_pipeline_if.env_m = 1'b0; + + + prv_pipeline_if.badaddr = '0; // TODO: Below signal is not being used!!! + prv_pipeline_if.wdata = '0; + prv_pipeline_if.addr = '0; + prv_pipeline_if.valid_write = '0; + prv_pipeline_if.wb_enable = '0; + prv_pipeline_if.instr = '0; + + // TODO: SIGNALS ARE NOT BEING USED!!! Below 3 signals will check the funct3 op code for an R-type instruction. Output of control unit, but not used in the priv unit + prv_pipeline_if.swap = '0; // CSRRW field which means to atomically swap values in the CSRs and integer registers + prv_pipeline_if.clr = '0; // CSRRC field which means to perform an atomic read and clear the bit in CSR + prv_pipeline_if.set = '0; // CSRRS where the instruction reads the value of the CSR, and writes it to integer register rd + + prv_pipeline_if.ex_rmgmt = '0; + prv_pipeline_if.ex_rmgmt_cause = '0; + + + + + prv_pipeline_if.mal_insn = 1'b1; + prv_pipeline_if.breakpoint = 1'b1; + // tb_expected_ex_src = BREAKPOINT; + tb_expected_exception = 1'b1; */ + + @(posedge CLK); + @(posedge CLK); + + nRST = 1; + + /*while (halt == 0 && clk_count != `RVB_CLK_TIMEOUT) begin + @(posedge CLK); + clk_count++; + if (gen_bus_if.addr == 16'h0000 & !gen_bus_if.busy & gen_bus_if.wen) + $write("%c",gen_bus_if.wdata[31:24]); + end + + #(1); + + dump_stats(); + dump_ram(); + + if (clk_count == `RVB_CLK_TIMEOUT) + $display("ERROR: Test timed out"); */ + + $finish; + + end : CORE_RUN + + /* task dump_stats(); + integer instret, cycles; + instret = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.instretfull; + cycles = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.cyclefull; + if (cycles != clk_count) $info("Cycles CSR != clk_count"); + stats_ptr = $fopen(`STATS_FILE_NAME, "w"); + $fwrite(stats_ptr, "Instructions retired: %2d\n", instret); + $fwrite(stats_ptr, "Cycles taken: %2d\n", cycles); + $fwrite(stats_ptr, "CPI: %5f\n", real'(cycles)/instret); + $fwrite(stats_ptr, "IPC: %5f\n", real'(instret)/cycles); + $fclose(stats_ptr); + endtask + + task dump_ram (); + ram_control = 0; + tb_gen_bus_if.addr = 0; + tb_gen_bus_if.ren = 0; + tb_gen_bus_if.wen = 0; + tb_gen_bus_if.wdata = 0; + tb_gen_bus_if.byte_en = 4'hf; + + fptr = $fopen(`OUTPUT_FILE_NAME, "w"); + + for(addr = 32'h80000000; addr < 32'h80007000; addr+=4) begin + read_ram(addr, data_temp); + #(PERIOD/4); + hexdump_temp = {8'h04, addr[15:0]>>2, 8'h00, data}; + checksum = calculate_crc(hexdump_temp); + if(data != 0) + $fwrite(fptr, ":%2h%4h00%8h%2h\n", 8'h4, addr[15:0]>>2, data, checksum); + end + // add the EOL entry to the file + $fwrite(fptr, ":00000001FF"); + + endtask + + task read_ram (input logic [31:0] raddr, output logic [31:0] rdata); + @(posedge CLK); + tb_gen_bus_if.addr = raddr; + tb_gen_bus_if.ren = 1; + @(posedge CLK); + while(tb_gen_bus_if.busy == 1) @(posedge CLK); + rdata = tb_gen_bus_if.rdata; + tb_gen_bus_if.ren = 0; + endtask + + function [7:0] calculate_crc (logic [63:0] hex_line); + static logic [7:0] checksum = 0; + int i; + + checksum = hex_line[7:0] + hex_line[15:8] + hex_line[23:16] + + hex_line[31:24] + hex_line[39:32] + hex_line[47:40] + + hex_line[55:48] + hex_line[63:56]; + + //take two's complement + checksum = (~checksum) + 1; + return checksum; + endfunction */ + +endmodule diff --git a/source_code/tb/tb_priv_1_11_control.sv b/source_code/tb/tb_priv_1_11_control.sv new file mode 100644 index 000000000..69a93e1ff --- /dev/null +++ b/source_code/tb/tb_priv_1_11_control.sv @@ -0,0 +1,198 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_RISCVBusiness.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Testbench for running RISCVBusiness until a halt condition. +* A hexdump of memory will occur after the halt condition. +*/ + +// Note: Figure out whether or not I need to change the name to Enes Shaltami. + +`timescale 1ns/100ps + +`include "priv_1_11_internal_if.vh" +`include "component_selection_defines.vh" + +`define OUTPUT_FILE_NAME "cpu.hex" +`define STATS_FILE_NAME "stats.txt" +`define RVB_CLK_TIMEOUT 10000 + +module tb_priv_1_11_control (); + + parameter PERIOD = 20; + import rv32i_types_pkg::*; + import machine_mode_types_1_11_pkg::*; + + logic CLK, nRST; + logic ram_control; // 1 -> CORE, 0 -> TB + logic halt; + logic [31:0] addr, data_temp, data; + logic [63:0] hexdump_temp; + logic [7:0] checksum; + //integer fpt output mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup,r, stats_ptr; + + + // Expected Outputs + logic tb_expected_mip_rup, tb_expected_mtval_rup, tb_expected_mcause_rup, tb_expected_mepc_rup, tb_expected_mstatus_rup, tb_expected_intr; + + mip_t tb_expected_mip_next; + mcause_t tb_expected_mcause_next; + mepc_t tb_expected_mepc_next; + mstatus_t tb_expected_mstatus_next; + mtval_t tb_expected_mtval_next; + + + //Interface Instantiations + priv_1_11_internal_if prv_internal_if(); + + //Module Instantiations + + priv_1_11_control DUT ( + .CLK(CLK), + .nRST(nRST), + .prv_intern_if(prv_internal_if) + ); // The modport is prv_control under the priv_1_11_internal_if.vh file + + + task reset_dut(); + nRST = 1'b0; + @(posedge CLK); + @(posedge CLK); + + nRST = 1'b1; + +/* Commenting out because package change made some signals unusable. Can be deleted later + // Resetting all of the inputs for the priv_block unit + prv_internal_if.pipe_clear = '0; + prv_internal_if.ret = '0; + prv_internal_if.epc = '0; + + // control signals for the exception combinational logic + prv_internal_if.fault_insn = 1'b0; + prv_internal_if.mal_insn = 1'b0; + prv_internal_if.illegal_insn = 1'b0; + prv_internal_if.fault_l = 1'b0; + prv_internal_if.mal_l = 1'b0; + prv_internal_if.fault_s = 1'b0; + prv_internal_if.mal_s = 1'b0; + prv_internal_if.breakpoint = 1'b0; + prv_internal_if.env_m = 1'b0; + + prv_internal_if.mepc = mepc_t'(32'h0); + prv_internal_if.mie = mie_t'(32'h0); // defined as a struct + prv_internal_if.mip = mip_t'(32'h0); // defined as a struct + prv_internal_if.mcause = mcause_t'(32'h0); + prv_internal_if.mstatus = mstatus_t'(32'h0); + prv_internal_if.clear_timer_int = 1'b0; + prv_internal_if.timer_int = 1'b0; + prv_internal_if.soft_int = 1'b0; + prv_internal_if.ext_int = 1'b0; + prv_internal_if.mtval = word_t'(32'h0); + + prv_internal_if.ex_rmgmt = 1'b0; + prv_internal_if.ex_rmgmt_cause = '0; + + // Expected Outputs + tb_expected_mcause_next.cause = ex_code_t'(31'h0); // INSN_MAL value + tb_expected_mcause_next.interrupt = 1'b0; // the source cannot be an interrupt + tb_expected_mip_rup = 1'b0; + tb_expected_mtval_rup = 1'b0; + tb_expected_mcause_rup = 1'b0; // neither an exception nor an interrupt occurred + tb_expected_mepc_rup = 1'b0; + tb_expected_mstatus_rup = 1'b0; + tb_expected_mip_next.mtip = 1'b0; + tb_expected_mip_next.msip = 1'b0; + tb_expected_mepc_next = 1'b0; // takes on value of epc + tb_expected_mstatus_next.ie = 1'b0; + tb_expected_mtval_next = 1'b0; + tb_expected_intr = 1'b0; */ + + endtask + + //Clock generation + initial begin : INIT + CLK = 0; + end : INIT + + always begin : CLOCK_GEN + #(PERIOD/2) CLK = ~CLK; + end : CLOCK_GEN + + //Setup core and let it run + initial begin : CORE_RUN + reset_dut(); + + // TODO: Expected output signals: mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup, mip_next, mcause_next, mepc_next, mstatus_next, mtval_next, intr + + // Test Case #1: Check mal instruction Exception + prv_internal_if.mal_insn = 1'b1; + prv_internal_if.breakpoint = 1'b1; // this will trigger an exception + tb_expected_mcause_next.cause = ex_code_t'(31'h0); // INSN_MAL value + tb_expected_mcause_next.interrupt = 1'b0; // the source cannot be an interrupt + tb_expected_intr = 1'b1; + tb_expected_mcause_rup = 1'b1; + tb_expected_mepc_rup = 1'b1; + tb_expected_mstatus_rup = 1'b1; + tb_expected_mtval_rup = 1'b1; + + + + prv_internal_if.fault_insn = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.illegal_insn = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.fault_l = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.fault_s = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.mal_s = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.mal_insn = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.ext_int = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.soft_int = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.timer_int = 1'b1; + + + + @(posedge CLK); + @(posedge CLK); + + nRST = 1; + + + + $finish; + + end : CORE_RUN + + + +endmodule diff --git a/source_code/wscript b/source_code/wscript index 6d6d0a0e6..e8ae5157d 100644 --- a/source_code/wscript +++ b/source_code/wscript @@ -19,6 +19,14 @@ def configure(ctx): tb = 'tb_RISCVBusiness_self_test', tb_includes = ['include'] ) + ctx.SFFUnits.add('RISCVBusiness_self_test_plic', + includes = ['include'], + src_dir = rvb_srcs, + use = ['RISCVBusiness_packages'], + tb_use = ['RISCVBusiness_packages'], + tb = 'tb_RISCVBusiness_self_test_plic', + tb_includes = ['include'] + ) ctx.SFFUnits.add('alu', includes = ['include'], src_dir = ['standard_core'], @@ -75,6 +83,24 @@ def configure(ctx): tb = 'tb_caches', tb_includes = ['include'] ) + + ctx.SFFUnits.add('priv_1_11_block', + includes = ['include'], + src_dir = ['privs/priv_1_11'], + use = ['RISCVBusiness_packages'], + tb_use = ['RISCVBusiness_packages'], + tb = 'tb_priv_1_11_block', + tb_includes = ['include'] + ) + ctx.SFFUnits.add('priv_1_11_control', + includes = ['include'], + src_dir = ['privs/priv_1_11'], + use = ['RISCVBusiness_packages'], + tb_use = ['RISCVBusiness_packages'], + tb = 'tb_priv_1_11_control', + tb_includes = ['include'] + ) + ctx.SFFUnits.add('sparce_svc', includes = ['include'], src_dir = ['sparce'], diff --git a/verification/interrupt-tests/ext_interrupt.S b/verification/interrupt-tests/ext_interrupt.S new file mode 100644 index 000000000..87a321d48 --- /dev/null +++ b/verification/interrupt-tests/ext_interrupt.S @@ -0,0 +1,171 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: ext_interrupt.S +* +* Created by: Enes Shaltami +* Email: ashaltam@purdue.edu +* Date Created: 03/15/2020 +* Description: Test of external interrupts +*/ + +#include "riscv_test.h" +#include "test_macros.h" + +#define claim_complete_reg 0x80040018 + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + +main: + la t0, mtvec_handler // load in address of mtvec_handler + ori t0, t0, 0x1 // or in the MODE (Vectored) + li TESTNUM, 0 // initialize TESTNUM (x28) + csrw mtvec, t0 // assign the supported vector handler into mtvec + + addi x3, x0, 0x1 + slli x3, x3, 11 // store 0x800 into x3 and x4 + csrw mie, x3 // Write value of 0x800 into mie, external interrupt enable for M-mode + + ori x4, x0, 0x1 + slli x4, x4, 3 + csrw mstatus, x4 // turn on the mie bit within mstatus + +idle_loop: + beq TESTNUM, x0, idle_loop // infinite loop will only finish once the external interrupt ISR has completed properly + + j done // jump to outside subroutine which will halt + //RVTEST_PASS + + +.align 2 +mtvec_handler: // General vector table for all exceptions and interrupts + // Interrupts must be disabled at entry to handler, could have jumps to different parts of code, potentially +IRQ_0: + j exception_handler // NOTE: PC for exception handler and user software interrupt handler are the same +IRQ_1: + j supervisor_software_interrupt_handler +IRQ_2: + j reserved_software_interrupt_handler +IRQ_3: + j machine_software_interrupt_handler +IRQ_4: + j user_timer_interrupt_handler +IRQ_5: + j supervisor_timer_interrupt_handler +IRQ_6: + j reserved_timer_interrupt_handler +IRQ_7: + j machine_timer_interrupt_handler +IRQ_8: + j user_external_interrupt_handler +IRQ_9: + j supervisor_external_interrupt_handler +IRQ_10: + j reserved_external_interrupt_handler +IRQ_11: + j machine_external_interrupt_handler + +exception_handler: // manage all exceptions here +supervisor_software_interrupt_handler: +reserved_software_interrupt_handler: +machine_software_interrupt_handler: +user_timer_interrupt_handler: +supervisor_timer_interrupt_handler: +reserved_timer_interrupt_handler: +machine_timer_interrupt_handler: +user_external_interrupt_handler: +supervisor_external_interrupt_handler: +reserved_external_interrupt_handler: + jal x1, push_registers + nop + jal x1, pop_registers + mret + +machine_external_interrupt_handler: + //jal x1, push_registers + addi x16, x0, 16 + addi x17, x0, 17 + addi x18, x0, 18 + addi x19, x0, 19 + addi x20, x0, 20 + + //la x2, claim_complete_reg // read the claim complete register to know the type of interrupt that occurred + lui x2, 0x80040 // load in the claim complete register address + addi x2, x2, 0x018 + lw x3, 0(x2) + beq x0, x3, invalid_id_read + li TESTNUM, 1 + sw x3, 0(x2) + +invalid_id_read: + //jal x1, pop_registers + mret + +push_registers: + // push RISC-V ABI registers + lui x2, 0x8 + ori x2, x2, 0x3FC + addi x2, x2, -64 // stack pointer + //sw x1, 4(x2) // return address + sw x5, 8(x2) // temporary registers + sw x6, 12(x2) + sw x7, 16(x2) + //sw x28, 20(x2) // disclude TESTNUM + sw x29, 24(x2) + sw x30, 28(x2) + sw x31, 32(x2) + sw x10, 36(x2) // function arguments + sw x11, 40(x2) + sw x12, 44(x2) + sw x13, 48(x2) + sw x14, 52(x2) + sw x15, 56(x2) + sw x16, 60(x2) + sw x17, 64(x2) + jr x1 + +pop_registers: + // pop registers + //lw x1, 4(x2) + lw x5, 8(x2) + lw x6, 12(x2) + lw x7, 16(x2) + //lw x28, 20(x2) + lw x29, 24(x2) + lw x30, 28(x2) + lw x31, 32(x2) + lw x10, 36(x2) + lw x11, 40(x2) + lw x12, 44(x2) + lw x13, 48(x2) + lw x14, 52(x2) + lw x15, 56(x2) + lw x16, 60(x2) + lw x17, 64(x2) + addi x2, x2, 64 + jr x1 + + + +RVTEST_CODE_END + +.data +RVTEST_DATA_BEGIN + TEST_DATA +RVTEST_DATA_END + diff --git a/verification/interrupt-tests/interrupts.S b/verification/interrupt-tests/interrupts.S new file mode 100644 index 000000000..a0bcd29f0 --- /dev/null +++ b/verification/interrupt-tests/interrupts.S @@ -0,0 +1,52 @@ +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U + +RVTEST_CODE_BEGIN + +main: + # Set up handler address + la t0, mtvec_handler + csrw mtvec, t0 + + # Enable Interrupts globally + addi x3, x0, 0x1 + slli x3, x3, 3 + csrw mstatus, x3 + + # Enable timer, software, external interrupts for machine mode + ori x3, x0, 0x8 + slli x3, x3, 8 + ori x3, x3, 0x88 + csrw mie, x3 + + # Load in magic sum: 3 + 7 + 11 corresponds to the values of the cause + # register for machine software, timer, and external interrupts respectively. + # This test will only pass if the 3 types are seen exactly once. + ori x6, x0, 21 + +loop: + # Wait for x6 to take on correct value + bne x6, x0, loop + RVTEST_PASS + +.align 2 +mtvec_handler: + csrr x7, mcause + andi x7, x7, 0xFF # Mask lower bits -- top bit indicates interrupt vs. exception + sub x6, x6, x7 + + # Reenable interrupts + #addi x3, x0, 0x1 + #slli x3, x3, 3 + #csrw mstatus, x3 + + mret + +RVTEST_CODE_END + +.data +RVTEST_DATA_BEGIN + TEST_DATA +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/timer2.S b/verification/interrupt-tests/soft_interrupt.S similarity index 71% rename from verification/self-tests/RV32I/timer2.S rename to verification/interrupt-tests/soft_interrupt.S index d3d694a81..8f044b37e 100644 --- a/verification/self-tests/RV32I/timer2.S +++ b/verification/interrupt-tests/soft_interrupt.S @@ -32,6 +32,7 @@ RVTEST_RV32U RVTEST_CODE_BEGIN +.equ msip, 0x80050000 main: # Define trap vector address @@ -40,41 +41,22 @@ main: addi x6, x0, 0xFF addi x7, x0, 0xFF + addi x3, x0, 0x1 + slli x3, x3, 3 csrw mstatus, x3 - la x1, mtime + + ori x3, x0, 0x8 + csrw mie, x3 + + la x1, msip lw x2, 0(x1) - addi x2, x2, 50 - la x1, mtimecmp + ori x2, x0, 1 sw x2, 0(x1) - ori x3, x0, 0x80 - csrw mie, x3 - #unique add insns that timer should interrupt - addi x24, x24, 1 - addi x24, x24, 2 - addi x24, x24, 3 - addi x24, x24, 4 - addi x24, x24, 5 - addi x24, x24, 6 - addi x24, x24, 7 - addi x24, x24, 8 - addi x24, x24, 9 - addi x24, x24, 10 - addi x24, x24, 11 - addi x24, x24, 12 - addi x24, x24, 13 - addi x24, x24, 14 - addi x24, x24, 15 - addi x24, x24, 16 - addi x24, x24, 17 - addi x24, x24, 18 - addi x24, x24, 19 - addi x24, x24, 20 - addi x9, x0, 210 - beq x9, x24, test_pass - addi x28, x0, 1 - RVTEST_FAIL -test_pass: + +idle_loop: + beq x6, x7, idle_loop + RVTEST_PASS .align 2 diff --git a/verification/self-tests/RV32I/.link.ld.swp b/verification/self-tests/RV32I/.link.ld.swp deleted file mode 100644 index 0b2547e8a3911aae232c562fc18baa60366daaea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI1&1(}u7>B0{TD5*qPwHh%54OeZZqkC)Ql*BX0c}g##DiejW;gB9bX&5UQd^=2 z>s3%ZC|2+vP!W%Uc=HRb7ZC&zp$A1oJ?Nnz`rB=`nWP(HYY+%)EK$$?Pm4 zgGwTLk{^&-8Ll0Sy?NPwYUBK)+2PZSjShucLr%QHr6={FjG+bXVZ#hbdeW4}vl%U` zOS+a7r=D{Y%CTr)Bylur8K!Po@`Ro-Qbtm>jWJWU^mHm{>y~ZFu|!KaDyI!|L`rL7 z7HX3LGO%I;SvwVM+sazPA;p>75ZKFi*2h^#a}mtVq`{A*3gYrab_WPl8i0Wv@a$N(8217v^^#bSr zfA9DIC-gsp_uw6P1@3}tzyfDL3^?-+gH{j%^?`;T-hvn4DR=_zgIRC`Tmol716bI^ z*iY~Sd;qV(ZEzD@2Q%OTP(cF3z!9(y>;mh-?^?_U-@!bX1JA)Ta0^@oSHNXJ7a1S} zWPl8i0W$Cx1~||0Tu;2eH{L(c8H}ZJgT@CiwGYf6oF`QZ>bEQcu7j15hV+73-{K$R6t1@x3bE)7G;Rz#I~2byedDl zei{6~0*#NQc-Ayb!&cMAl&*12wN-BEL;9$0+Pqn2JjZx*qok?6S&M@HUIPB%%4A4UlqOe+ zdobE+e!aFnX?tO(ErccKh!x*+1Ni}XC@Pa-MQLZ#tgkE5AMNSxW32E<^_~Lm8U6?F CTZIb% diff --git a/verification/self-tests/RV32I/misa.S b/verification/self-tests/RV32I/misa.S new file mode 100644 index 000000000..b166e3310 --- /dev/null +++ b/verification/self-tests/RV32I/misa.S @@ -0,0 +1,37 @@ +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + # Record original MISA value + csrr x1, misa + + # Make a bad value for MISA + ori x2, x0, -1 + + # Attempt to write, read back + csrw misa, x2 + csrr x2, misa + + bne x1, x2, failure + + # Attempt to write a good value to MISA - Enable "A" extension + csrr x2, misa + ori x2, x2, 0x1 + csrw misa, x2 + csrr x1, misa + + bne x1, x2, failure + + RVTEST_PASS + +failure: + RVTEST_FAIL + +RVTEST_CODE_END + +.data +RVTEST_DATA_BEGIN + TEST_DATA +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/timer.S b/verification/self-tests/RV32I/timer.S index 5c7caf9a7..0862fd255 100644 --- a/verification/self-tests/RV32I/timer.S +++ b/verification/self-tests/RV32I/timer.S @@ -31,6 +31,8 @@ RVTEST_RV32U RVTEST_CODE_BEGIN +.equ mtime, 0x80050004 +.equ mtimecmp, 0x8005000C main: # Define trap vector address @@ -40,11 +42,12 @@ main: addi x6, x0, 0xFF addi x7, x0, 0xFF addi x3, x0, 0x1 + slli x3, x3, 3 csrw mstatus, x3 - la x1, mtime + li x1, mtime lw x2, 0(x1) addi x2, x2, 50 - la x1, mtimecmp + li x1, mtimecmp sw x2, 0(x1) ori x3, x0, 0x80 csrw mie, x3 From 8a8a5bf25615d267a35e9990f02c538a87b9e6a3 Mon Sep 17 00:00:00 2001 From: Cole Nelson <35197931+cole-nelson@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:46:41 -0400 Subject: [PATCH 04/47] Bus team (#3) Updated AHB Master with support for waited transfers --- source_code/bus_bridges/ahb.sv | 84 +- source_code/tb/tb_memory_controller.sv | 13 +- source_code/uvm_tb/ahb_master/ahb_if.vh | 38 + source_code/uvm_tb/ahb_master/compile.sh | 1 + .../uvm_tb/ahb_master/generic_bus_if.vh | 51 ++ source_code/uvm_tb/ahb_master/run.sh | 1 + .../uvm_tb/ahb_master/rv32i_types_pkg.sv | 206 +++++ source_code/uvm_tb/ahb_master/tb_ahb.sv | 42 + .../uvm_tb/ahb_master/tb_components.svh | 724 ++++++++++++++++++ 9 files changed, 1114 insertions(+), 46 deletions(-) create mode 100644 source_code/uvm_tb/ahb_master/ahb_if.vh create mode 100755 source_code/uvm_tb/ahb_master/compile.sh create mode 100644 source_code/uvm_tb/ahb_master/generic_bus_if.vh create mode 100755 source_code/uvm_tb/ahb_master/run.sh create mode 100644 source_code/uvm_tb/ahb_master/rv32i_types_pkg.sv create mode 100644 source_code/uvm_tb/ahb_master/tb_ahb.sv create mode 100644 source_code/uvm_tb/ahb_master/tb_components.svh diff --git a/source_code/bus_bridges/ahb.sv b/source_code/bus_bridges/ahb.sv index 86009e4be..6de00fcbc 100644 --- a/source_code/bus_bridges/ahb.sv +++ b/source_code/bus_bridges/ahb.sv @@ -36,7 +36,7 @@ module ahb ( IDLE, DATA } state_t; - + state_t state, n_state; always_ff @ (posedge CLK, negedge nRST) begin @@ -45,8 +45,13 @@ module ahb ( else state <= n_state; end - - assign n_state = (out_gen_bus_if.ren | out_gen_bus_if.wen) ? DATA : IDLE; + + always_comb begin + if((state == DATA) & !(ahb_m.HREADY)) + n_state = state; + else + n_state = out_gen_bus_if.ren | out_gen_bus_if.wen ? DATA : IDLE; + end always_comb begin if(out_gen_bus_if.byte_en == 4'b1111) @@ -56,45 +61,44 @@ module ahb ( else ahb_m.HSIZE = 3'b000; // byte end + + always_comb + begin + if(out_gen_bus_if.ren) begin + ahb_m.HTRANS = 2'b10; + ahb_m.HWRITE = 1'b0; + ahb_m.HADDR = out_gen_bus_if.addr; + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HBURST = 0; + ahb_m.HPROT = 0; + ahb_m.HMASTLOCK = 0; + end + else if(out_gen_bus_if.wen) begin + ahb_m.HTRANS = 2'b10; + ahb_m.HWRITE = 1'b1; + ahb_m.HADDR = out_gen_bus_if.addr; + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HBURST = 0; + ahb_m.HPROT = 0; + ahb_m.HMASTLOCK = 0; + end + else begin + ahb_m.HTRANS = 2'b0; + ahb_m.HWRITE = 1'b0; + ahb_m.HADDR = 0; + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HBURST = 0; + ahb_m.HPROT = 0; + ahb_m.HMASTLOCK = 0; + end + + if(state == DATA) begin + ahb_m.HWDATA = out_gen_bus_if.wdata; + end + end - always_comb - begin - //-- Read Request --// - if ( out_gen_bus_if.ren ) - begin - ahb_m.HTRANS = 2'b10; - ahb_m.HWRITE = 1'b0; - ahb_m.HADDR = out_gen_bus_if.addr; - ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HPROT = 0; - ahb_m.HMASTLOCK = 0; - end - //-- Write Request --// - else if ( out_gen_bus_if.wen ) - begin - ahb_m.HTRANS = 2'b10; - ahb_m.HWRITE = 1'b1; - ahb_m.HADDR = out_gen_bus_if.addr; - ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HPROT = 0; - ahb_m.HMASTLOCK = 0; - end - //-- Default : Not reading / writing --// - else - begin - ahb_m.HTRANS = 2'b00; - ahb_m.HWRITE = 1'b0; - ahb_m.HADDR = 0; - ahb_m.HWDATA = 0; - ahb_m.HBURST = 0; - ahb_m.HPROT = 0; - ahb_m.HMASTLOCK = 0; - end - end - assign out_gen_bus_if.busy = ~(ahb_m.HREADY & (state == DATA)); + assign out_gen_bus_if.busy = state == IDLE || ~((ahb_m.HREADY && (state == DATA))); assign out_gen_bus_if.rdata = ahb_m.HRDATA; endmodule diff --git a/source_code/tb/tb_memory_controller.sv b/source_code/tb/tb_memory_controller.sv index 563a97a1d..9377a48c5 100644 --- a/source_code/tb/tb_memory_controller.sv +++ b/source_code/tb/tb_memory_controller.sv @@ -39,7 +39,7 @@ module memory_controller_tb (); ahb_if ahb_m(); memory_controller DUT ( CLK, nRST, d_gen_bus_if, i_gen_bus_if, out_gen_bus_if ); - ahb_master DUT2 ( CLK, nRST, ahb_m, out_gen_bus_if); + ahb DUT2 ( CLK, nRST, ahb_m, out_gen_bus_if); //-- CLOCK INITIALIZATION --// initial begin : INIT @@ -98,8 +98,9 @@ module memory_controller_tb (); i_gen_bus_if.wen = 0; i_gen_bus_if.addr = i_gen_bus_if.addr + 4; #(5 * PERIOD) + @(posedge CLK) ahb_m.HRDATA = 32'hDEADBEEF; - ahb_m.HWDATA = 0; + ahb_m.HWDATA = 0; ahb_m.HREADY = 1; #(PERIOD) ahb_m.HRDATA = 32'hbad1bad1; @@ -121,7 +122,7 @@ module memory_controller_tb (); #(5 * PERIOD) ahb_m.HRDATA = 32'hDEADBEEF; ahb_m.HWDATA = 0; - ahb_m.HREADY = 1; + ahb_m.HREADY = 0; #(PERIOD) ahb_m.HRDATA = 32'hDEADBEEF; ahb_m.HREADY = 0; @@ -132,7 +133,7 @@ module memory_controller_tb (); #(5 * PERIOD) ahb_m.HRDATA = 32'hbad1bad1; ahb_m.HWDATA = 0; - ahb_m.HREADY = 1; + ahb_m.HREADY = 0; #(PERIOD) ahb_m.HRDATA = 32'hbad1bad1; ahb_m.HREADY = 0; @@ -156,7 +157,7 @@ module memory_controller_tb (); ahb_m.HREADY = 1; #(PERIOD) ahb_m.HRDATA = 32'hDEADBEEF; - ahb_m.HREADY = 0; + ahb_m.HREADY = 1; d_gen_bus_if.ren = 0; d_gen_bus_if.wen = 1; i_gen_bus_if.ren = 1; @@ -167,7 +168,7 @@ module memory_controller_tb (); ahb_m.HREADY = 1; #(PERIOD) ahb_m.HRDATA = 32'hbad1bad1; - ahb_m.HREADY = 0; + ahb_m.HREADY = 1; d_gen_bus_if.ren = 0; d_gen_bus_if.wen = 0; i_gen_bus_if.ren = 1; diff --git a/source_code/uvm_tb/ahb_master/ahb_if.vh b/source_code/uvm_tb/ahb_master/ahb_if.vh new file mode 100644 index 000000000..e1c75bf5c --- /dev/null +++ b/source_code/uvm_tb/ahb_master/ahb_if.vh @@ -0,0 +1,38 @@ +// File name: ahb_if.vh +// Created: 9/10/2015 +// Author: Erin Rasmussen +// Version 1.0 +// Description: Interface for fully specified AMBA 3 AHB-Lite + +`ifndef AHB_IF_VH +`define AHB_IF_VH + +interface ahb_if(input logic HCLK); + logic [1:0] HTRANS, HRESP; + logic [2:0] HSIZE; + logic [31:0] HADDR; + logic [31:0] HWDATA; + logic HWRITE, HREADY,HREADYOUT, HSEL, HMASTLOCK; + logic [31:0] HRDATA; + logic [2:0] HBURST; + logic [3:0] HPROT; + + modport ahb_s ( + input HTRANS, HWRITE, HADDR, HWDATA, HSIZE, HSEL, HBURST, HREADY, + HPROT, HMASTLOCK, + output HREADYOUT, HRESP, HRDATA + ); + + modport ahb_m ( + input HREADY, HRESP, HRDATA, + output HTRANS, HWRITE, HADDR, HWDATA, HSIZE, HBURST, HPROT, HMASTLOCK + ); + + modport ahb_m_i ( + output HREADY, HRESP, HRDATA, + input HTRANS, HWRITE, HADDR, HWDATA, HSIZE, HBURST, HPROT, HMASTLOCK + ); + +endinterface // ahb_if + +`endif // `ifndef AHB_IF_VH diff --git a/source_code/uvm_tb/ahb_master/compile.sh b/source_code/uvm_tb/ahb_master/compile.sh new file mode 100755 index 000000000..a99b6ebe0 --- /dev/null +++ b/source_code/uvm_tb/ahb_master/compile.sh @@ -0,0 +1 @@ +vlog -L $QUESTA_HOME/uvm-1.2 tb_ahb.sv diff --git a/source_code/uvm_tb/ahb_master/generic_bus_if.vh b/source_code/uvm_tb/ahb_master/generic_bus_if.vh new file mode 100644 index 000000000..f4d36110e --- /dev/null +++ b/source_code/uvm_tb/ahb_master/generic_bus_if.vh @@ -0,0 +1,51 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: generic_bus_if.vh +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Interface for connecting a requestor to ram. +*/ + +`ifndef GENERIC_BUS_IF_VH +`define GENERIC_BUS_IF_VH +`include "rv32i_types_pkg.sv" //for testbench purpose + +interface generic_bus_if (input logic clk); + import rv32i_types_pkg::*; + + logic [RAM_ADDR_SIZE-1:0] addr; + word_t wdata; + word_t rdata; + logic ren,wen; + logic busy; + logic [3:0] byte_en; + + modport generic_bus ( + input addr, ren, wen, wdata, byte_en, + output rdata, busy + ); + + modport cpu ( + input rdata, busy, + output addr, ren, wen, wdata, byte_en + ); + +endinterface + +`endif //GENERIC_BUS_IF_VH diff --git a/source_code/uvm_tb/ahb_master/run.sh b/source_code/uvm_tb/ahb_master/run.sh new file mode 100755 index 000000000..bc398d079 --- /dev/null +++ b/source_code/uvm_tb/ahb_master/run.sh @@ -0,0 +1 @@ +vsim -i tb_ahb -L $QUESTA_HOME/uvm-1.2 -novopt -do wave.do diff --git a/source_code/uvm_tb/ahb_master/rv32i_types_pkg.sv b/source_code/uvm_tb/ahb_master/rv32i_types_pkg.sv new file mode 100644 index 000000000..43968cc7e --- /dev/null +++ b/source_code/uvm_tb/ahb_master/rv32i_types_pkg.sv @@ -0,0 +1,206 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32i_types_pkg.sv +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/01/2016 +* Description: Package containing types used for a RV32I implementation +*/ + +`ifndef RV32I_TYPES_PKG_SV +`define RV32I_TYPES_PKG_SV +package rv32i_types_pkg; + parameter WORD_SIZE = 32; + parameter RAM_ADDR_SIZE = 32; + parameter OP_W = 7; + parameter BR_W = 3; + parameter LD_W = 3; + parameter SW_W = 3; + parameter IMM_W = 3; + parameter REG_W = 3; + + typedef logic [WORD_SIZE-1:0] word_t; + + typedef enum logic [OP_W-1:0] { + LUI = 7'b0110111, + AUIPC = 7'b0010111, + JAL = 7'b1101111, + JALR = 7'b1100111, + // All branching instructions share an opcode + BRANCH = 7'b1100011, + // All load instructions share an opcode + LOAD = 7'b0000011, + // All store instructions share an opcode + STORE = 7'b0100011, + // All immediate ALU instructions share an opcode + IMMED = 7'b0010011, + // All register-register instructions share an opcode + REGREG = 7'b0110011, + // All system instructions share an opcode + SYSTEM = 7'b1110011, + MISCMEM = 7'b0001111 + } opcode_t; + + typedef enum logic [BR_W-1:0] { + BEQ = 3'b000, + BNE = 3'b001, + BLT = 3'b100, + BGE = 3'b101, + BLTU = 3'b110, + BGEU = 3'b111 + } branch_t; + + typedef enum logic [LD_W-1:0] { + LB = 3'b000, + LH = 3'b001, + LW = 3'b010, + LBU = 3'b100, + LHU = 3'b101 + } load_t; + + typedef enum logic [SW_W-1:0] { + SB = 3'b000, + SH = 3'b001, + SW = 3'b010 + } store_t; + + typedef enum logic [IMM_W-1:0] { + ADDI = 3'b000, + SLTI = 3'b010, + SLTIU = 3'b011, + XORI = 3'b100, + ORI = 3'b110, + ANDI = 3'b111, + SLLI = 3'b001, + // Logical/Arithmetic based on bit 30 of instruction + // 0 / 1 + SRI = 3'b101 + } imm_t; + + typedef enum logic [REG_W-1:0] { + // Add/Sub based on bit 30 of instruction + // 0 / 1 + ADDSUB = 3'b000, + SLL = 3'b001, + SLT = 3'b010, + SLTU = 3'b011, + XOR = 3'b100, + // Logical/Arithmetic based on bit 30 of instruction + // 0 / 1 + SR = 3'b101, + OR = 3'b110, + AND = 3'b111 + } regreg_t; + + typedef enum logic [2:0] { + // Non CSR contains ECALL, EBREAK, and xRET instructions + // ECALL/EBREAK based on bit 20 of instruction + // 0 / 1 + // xRET based on bits 28 and 29 of instruction + PRIV = 3'b000, + CSRRW = 3'b001, + CSRRS = 3'b010, + CSRRC = 3'b011, + CSRRWI = 3'b101, + CSRRSI = 3'b110, + CSRRCI = 3'b111 + } rv32i_system_t; + + typedef enum logic [11:0] { + ECALL = 12'b0000000_00000, + EBREAK = 12'b0000000_00001, + MRET = 12'b0011000_00010, + SRET = 12'b0001000_00010, + URET = 12'b0000000_00010 + } priv_insn_t; + + typedef enum logic [2:0] { + FENCE = 3'b000, + FENCEI = 3'b001 + } rv32i_miscmem_t; + + typedef struct packed { + logic [6:0] funct7; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } rtype_t; + + typedef struct packed { + logic [11:0] imm11_00; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } itype_t; + + typedef struct packed { + logic [6:0] imm11_05; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] imm04_00; + opcode_t opcode; + } stype_t; + + typedef struct packed { + logic imm12; + logic [5:0] imm10_05; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [3:0] imm04_01; + logic imm11; + opcode_t opcode; + } sbtype_t; + + typedef struct packed { + logic [19:0] imm31_12; + logic [4:0] rd; + opcode_t opcode; + } utype_t; + + typedef struct packed { + logic imm20; + logic [9:0] imm10_01; + logic imm11; + logic [7:0] imm19_12; + logic [4:0] rd; + opcode_t opcode; + } ujtype_t; + + typedef struct packed { + logic [11:0] csr; + logic [4:0] rs1_zimm; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } systype_t; + + typedef struct packed { + logic token; + word_t pc; + word_t pc4; + word_t instr; + word_t prediction; + } fetch_ex_pipeline_reg_t; + +endpackage +`endif diff --git a/source_code/uvm_tb/ahb_master/tb_ahb.sv b/source_code/uvm_tb/ahb_master/tb_ahb.sv new file mode 100644 index 000000000..c23828e1b --- /dev/null +++ b/source_code/uvm_tb/ahb_master/tb_ahb.sv @@ -0,0 +1,42 @@ +//By : Zhengsen Fu +//Description : tb for ahb master but without burst mode +//Last Updated : 8/21/20 + +`include "ahb.sv" +`include "uvm_macros.svh" +`include "tb_components.svh" +import uvm_pkg::*; + +module tb_ahb (); + logic clk; + logic n_rst; + + // generate clock + initial begin + clk = 0; + forever #10 clk = !clk; + end + + ahb_if ahbif(clk); + generic_bus_if bus_if(clk); + + ahb DUT(.CLK(clk), .nRST(n_rst), .ahb_m(ahbif.ahb_m), .out_gen_bus_if(bus_if.generic_bus)); + + // reset DUT + initial begin + n_rst = 0; + @(posedge clk); + n_rst = 1; + end + + initial begin + uvm_config_db#(virtual ahb_if)::set( null, "", "ahb_vif", ahbif); + uvm_config_db#(virtual generic_bus_if)::set( null, "", "bus_vif", bus_if); + // uvm_config_db#(logic)::set( null, "", "n_rst", n_rst); + run_test("ahb_test"); // initiate test component + + end + +endmodule + + diff --git a/source_code/uvm_tb/ahb_master/tb_components.svh b/source_code/uvm_tb/ahb_master/tb_components.svh new file mode 100644 index 000000000..58e5d9e31 --- /dev/null +++ b/source_code/uvm_tb/ahb_master/tb_components.svh @@ -0,0 +1,724 @@ +//By : Zhengsen Fu +//Description : all the UVM components needed in tb_ahb.sv +//Last Updated : 8/21/20 + +`include "uvm_macros.svh" +import uvm_pkg::*; + + +class transaction extends uvm_sequence_item; + // parameter RAM_SIZE = 256; + localparam IDLE = '0; + localparam READ = 2'b01; + localparam WRITE = 2'b10; + + rand bit[1:0] trans; //trasaction type. Possible values indicated above + rand logic [31:0] wdata; + rand logic [31:0] addr; + rand logic [3:0] byte_en; + + `uvm_object_utils_begin(transaction) + `uvm_field_int(trans, UVM_DEFAULT) + `uvm_field_int(wdata, UVM_DEFAULT) + `uvm_field_int(addr, UVM_DEFAULT) + `uvm_field_int(byte_en, UVM_DEFAULT) + `uvm_object_utils_end + + constraint address_limit {byte_en == 4'b1111 -> addr <= 4'hf - 3; + byte_en == 4'b0011 || byte_en == 4'b1100 -> addr <= 4'hf - 1;} + constraint trans_constraint {trans == IDLE || trans == READ || trans == WRITE;} + constraint addr_size {addr <= 4'hf;} //testing with a simulated memory smaller than 16 registers + + function new(string name = "transaction"); + super.new(name); + endfunction: new +endclass //transaction + + +//a queue that record all the transactions. +//search function still needs debugging +class transactionSeq; //transaction sequence + localparam MAX_SIZE = 4000; + transaction arr[MAX_SIZE - 1:0]; + integer time_arr[MAX_SIZE - 1:0]; //arr which recording the time that the transaction has happened + int index; //points to most recent transactoin + function new(); + index = -1; + endfunction //new() + + //push one transaction into the arr + function void push(transaction item); + if(index == MAX_SIZE) begin //can be implemented as a queue is needed in the future + $fatal("transactionSeq: sequence cannot hold more items"); + end + index++; + arr[index] = item; + time_arr[index] = $time(); + endfunction + + //search for the most recent transaction that write to an addr + function transaction search(logic[31:0] addr); + transaction item; + for(int lcv = index; lcv > 0; lcv--) begin + item = arr[lcv]; + //if not WRITE, pass + if (item.trans != item.WRITE) begin + continue; + end + if(item.byte_en == 4'b1111) begin + if (addr == item.addr || addr == (item.addr - 1) || addr == (item.addr - 2) || addr == (item.addr - 3)) begin + return item; + end + end else if(item.byte_en == 4'b1100 || item.byte_en == 4'b0011) begin + if (addr == item.addr || addr == (item.addr - 1)) begin + return item; + end + end else begin + if (addr == item.addr) begin + return item; + end + end + end + $fatal("transaction not found!\n"); + endfunction + + //search for the index of the most recent transaction that write to an addr + function int search_index(logic[31:0] addr); + transaction item; + for(int lcv = index; lcv > 0; lcv--) begin + item = arr[lcv]; + //if not WRITE, pass + if (item.trans != item.WRITE) begin + continue; + end + if(item.byte_en == 4'b1111) begin + if (addr == item.addr || addr == (item.addr - 1) || addr == (item.addr - 2) || addr == (item.addr - 3)) begin + return lcv; + end + end else if(item.byte_en == 4'b1100 || item.byte_en == 4'b0011) begin + if (addr == item.addr || addr == (item.addr - 1)) begin + return lcv; + end + end else begin + if (addr == item.addr) begin + return lcv; + end + end + end + $fatal("transaction not found!\n"); + endfunction + + //search for the time of the most recent transaction that write to an addr + function int search_time(logic[31:0] addr); + transaction item; + for(int lcv = index; lcv > 0; lcv--) begin + item = arr[lcv]; + //if not WRITE, pass + if (item.trans != item.WRITE) begin + continue; + end + if(item.byte_en == 4'b1111) begin + if (addr == item.addr || addr == (item.addr - 1) || addr == (item.addr - 2) || addr == (item.addr - 3)) begin + return time_arr[lcv]; + end + end else if(item.byte_en == 4'b1100 || item.byte_en == 4'b0011) begin + if (addr == item.addr || addr == (item.addr - 1)) begin + return time_arr[lcv]; + end + end else begin + if (addr == item.addr) begin + return time_arr[lcv]; + end + end + end + // $fatal("transaction not found!\n"); + endfunction +endclass //transactionSeq + + + +//simulated memory that is used in the simulated slave +class sim_mem extends uvm_object; + parameter SIZE = 4; //the number of bits in the addr + rand bit [7:0] registers[2 ** SIZE - 1:0]; + + `uvm_object_utils_begin(sim_mem) + `uvm_field_sarray_int(registers, UVM_DEFAULT) + `uvm_object_utils_end + + function new(string name = "sim_mem"); + super.new(name); + endfunction: new + + //print the value in all registers for debugging purpose + function void print_all(); + for (int lcv = 0; lcv < 2**SIZE; lcv++) begin + $display("%h: %h",lcv, registers[lcv]); + end + + endfunction + + //write the least significant byte in HWDATA to addr + function void write_byte(logic[SIZE - 1:0] addr, logic[31:0] HWDATA); + registers[addr] = HWDATA[7:0]; + endfunction + + //write two least significant bytes in HWDATA to addr + function void write_half(logic[SIZE - 1:0] addr, logic[31:0] HWDATA); + registers[addr++] = HWDATA[7:0]; + registers[addr] = HWDATA[15:8]; + endfunction + + //write four least significant bytes in HWDATA to addr + function void write_word(logic[SIZE - 1:0] addr, logic[31:0] HWDATA); + registers[addr++] = HWDATA[7:0]; + registers[addr++] = HWDATA[15:8]; + registers[addr++] = HWDATA[23:16]; + registers[addr] = HWDATA[31:24]; + endfunction + + //read a byte stored in addr to the least significant byte to HRDATA + function logic[31:0] read_byte(logic[SIZE - 1:0] addr); + logic[31:0] result; + result = '0; + result[7:0] = registers[addr]; + return result; + endfunction + + //read two bytes stored in addr to the least significant byte to HRDATA + function logic[31:0] read_half(logic[SIZE - 1:0] addr); + logic[31:0] result; + result = '0; + result[7:0] = registers[addr++]; + result[15:8] = registers[addr]; + return result; + endfunction + + //read four bytes stored in addr to the least significant byte to HRDATA + function logic[31:0] read_word(logic[SIZE - 1:0] addr); + logic[31:0] result; + result = '0; + result[7:0] = registers[addr++]; + result[15:8] = registers[addr++]; + result[23:16] = registers[addr++]; + result[31:24] = registers[addr]; + return result; + endfunction +endclass //sim_mem + + + +// simulated slave interface +class sim_slave extends uvm_monitor; + `uvm_component_utils(sim_slave) + sim_mem slave_mem; + sim_mem input_mem; //simulated memory from ahb_env + logic rand_ready; //randomized wait between transactions + virtual ahb_if ahbif; + transaction prev_tx; //transaction in the previous clk cycle + logic [2:0] prev_HSIZE; //HSIZE in the previous clk cycle + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction: new + + //randomly generate a slave busy of random length + task random_wait(); + rand_ready = $urandom(); + while(rand_ready) begin + ahbif.HREADY = 0; + @(posedge ahbif.HCLK); + rand_ready = $urandom(); + end + ahbif.HREADY = 1; + endtask + + virtual function void build_phase(uvm_phase phase); + slave_mem = sim_mem::type_id::create("slave_mem"); + prev_tx = transaction::type_id::create("prev_tx"); + + // get interface + if (!uvm_config_db#(virtual ahb_if)::get(this, "", "ahb_vif", ahbif)) begin + `uvm_fatal("sim_slave", "No virtual interface specified for this monitor instance") + end + + // get sim_mem from ahb_slave + if (!uvm_config_db#(sim_mem)::get(this, "", "slave_mem", input_mem)) begin + `uvm_fatal("sim_slave", "No input mem") + end + //copy so that they will have the same initial value + slave_mem.copy(input_mem); + endfunction + + task run_phase(uvm_phase phase); + super.run_phase(phase); + ahbif.HREADY = 1; + + //TODO:missing HRESP + forever begin + @(posedge ahbif.HCLK); + // $info("SLAVE DEBUG prev trans: %h addr: %h", prev_tx.trans, prev_tx.addr); + + //data phase: responses from the slave + if(prev_tx.trans == '0) begin //if previous transaction is IDLE, do nothing + ; + end else if(prev_tx.trans == prev_tx.WRITE) begin //if previous transaction is WRITE, write HWDATA to mem + random_wait(); + #2; // wait two nano seconds before sample HWDATA + // $info("SLAVE DEBUG write addr: %h value: %h",prev_tx.addr, ahbif.HWDATA); + case(prev_HSIZE) + 3'b000: slave_mem.write_byte(prev_tx.addr, ahbif.HWDATA); //byte + 3'b001: slave_mem.write_half(prev_tx.addr, ahbif.HWDATA); //half word + 3'b010: slave_mem.write_word(prev_tx.addr, ahbif.HWDATA); //word + endcase + end else begin //if previous transaction is READ, put the data onto HRDATA + random_wait(); + case(prev_HSIZE) + 3'b000: ahbif.HRDATA = slave_mem.read_byte(prev_tx.addr); //byte + 3'b001: ahbif.HRDATA = slave_mem.read_half(prev_tx.addr); //half word + 3'b010: ahbif.HRDATA = slave_mem.read_word(prev_tx.addr); //word + endcase + // $info("SLAVE DEBUG read addr: %h value: %h size: %h",prev_tx.addr, ahbif.HRDATA, prev_HSIZE); + // slave_mem.print_all(); + end + + //addr phase: samples transaction details + #2; + if (ahbif.HTRANS == '0) begin + prev_tx.trans = prev_tx.IDLE; + end + else if (ahbif.HWRITE) begin + prev_tx.trans = prev_tx.WRITE; + end + else begin + prev_tx.trans = prev_tx.READ; + end + prev_tx.addr = ahbif.HADDR; + prev_tx.wdata = ahbif.HWDATA; + prev_HSIZE = ahbif.HSIZE; + + end + endtask + +endclass //sim_slave + + + +//simulated cpu which uses DUT (ahb master) to communicate with the simulated slave +class sim_cpu extends uvm_driver#(transaction); + `uvm_component_utils(sim_cpu) + + virtual generic_bus_if bus_if; + + uvm_analysis_port #(transaction) cpu_ap; //writes addr phase transaction to predictor + uvm_analysis_port #(transaction) response_ap; //writes data phase READ transaction to comparator + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction: new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + cpu_ap = new("cpu_ap", this); + response_ap = new("response_ap", this); + + // get interface from database + if( !uvm_config_db#(virtual generic_bus_if)::get(this, "", "bus_vif", bus_if) ) begin + // if the interface was not correctly set, raise a fatal message + `uvm_fatal("simulated cpu", "No virtual interface specified for this test instance"); + end + + endfunction: build_phase + + task run_phase(uvm_phase phase); + transaction req_item; + transaction prev; //previous addr phase transaction + transaction response; //data phase response + bit busy_wait = 0; //whether there is or not a wait within addr phase of this transaction + + prev = transaction::type_id::create("prev"); + response = transaction::type_id::create("response"); + + forever begin + seq_item_port.get_next_item(req_item); + + //if there was an wait in previous loop + //then remove an extra wait and clear busy_wait + if(!busy_wait) + @(posedge bus_if.clk); + else + busy_wait = 0; + + //at the start of the simulation, do not sending void transaction + if(prev.addr !== 'x) + cpu_ap.write(prev); + + //addr phase + if(req_item.trans == req_item.IDLE) begin + bus_if.ren = 0; + bus_if.wen = 0; + // bus_if.addr = req_item.addr; + // bus_if.wdata = req_item.wdata; + // bus_if.byte_en = req_item.byte_en; + end + else if(req_item.trans == req_item.READ) begin + bus_if.ren = 1; + bus_if.wen = 0; + bus_if.addr = req_item.addr; + // bus_if.wdata = req_item.wdata; + bus_if.byte_en = req_item.byte_en; + end + else if(req_item.trans == req_item.WRITE) begin + bus_if.ren = 0; + bus_if.wen = 1; + bus_if.addr = req_item.addr; + // bus_if.wdata = req_item.wdata; + bus_if.byte_en = req_item.byte_en; + end + + //data phase + if(prev.trans == prev.WRITE) begin + bus_if.wdata = prev.wdata; + end + + #1; + if(!bus_if.busy) begin + if(prev.trans == req_item.READ) begin + response.copy(prev); + response.wdata = bus_if.rdata; + response_ap.write(response); + end + end + + seq_item_port.item_done(); + //if busy is asserted by the slave, wait until busy is cleared + while (bus_if.busy) begin + if(req_item.trans == req_item.IDLE) begin + break; + end + @(posedge bus_if.clk); + busy_wait = 1; + if(prev.trans == req_item.READ) begin + #1; + if(!bus_if.busy) begin + // $info("CPU DEBUG rear busy reading point"); //DEBUG + response.copy(prev); + response.wdata = bus_if.rdata; + response_ap.write(response); + @(posedge bus_if.clk); + end + end + end + prev.copy(req_item); + end + endtask + +endclass //sim_cpu + + + +class ahb_seq extends uvm_sequence #(transaction); + `uvm_object_utils(ahb_seq) + + int number; + + function new(string name = ""); + super.new(name); + endfunction: new + + task body(); + transaction req_item; + req_item = transaction::type_id::create("req_item"); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.READ; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.WRITE; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.WRITE; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.IDLE; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.WRITE; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.READ; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.READ; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.IDLE; + finish_item(req_item); + + + number = 0; + repeat(3000) begin + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + number++; + $info("DEBUG: item sent! %d", number); + + finish_item(req_item); + end + $info("DEBUG: reached end"); + endtask: body +endclass //ahb_seq + + + +class sequencer extends uvm_sequencer#(transaction); + `uvm_component_utils(sequencer) + function new(input string name= "sequencer", uvm_component parent=null); + super.new(name, parent); + endfunction : new +endclass : sequencer + + + +class ahb_agent extends uvm_agent; + `uvm_component_utils(ahb_agent) + sim_cpu cpu; + sim_slave slave; + sequencer sqr; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + sqr = sequencer::type_id::create("sqr", this); + slave = sim_slave::type_id::create("slave", this); + cpu = sim_cpu::type_id::create("cpu", this); + endfunction + + virtual function void connect_phase(uvm_phase phase); + cpu.seq_item_port.connect(sqr.seq_item_export); + endfunction +endclass //ahb_agent + + + +class predictor extends uvm_subscriber #(transaction); + `uvm_component_utils(predictor) + uvm_analysis_port #(transaction) pred_ap; + sim_mem mem; + logic [2:0] HSIZE; + transactionSeq tx_seq; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction: new + + function void build_phase(uvm_phase phase); + pred_ap = new("pred_ap", this); + endfunction + + function void write(transaction t); + tx_seq.push(t); + //TODO: need to verify + if(t.byte_en == 4'b1111) + HSIZE = 3'b010; // word + else if(t.byte_en == 4'b1100 || t.byte_en == 4'b0011) + HSIZE = 3'b001; // half word + else + HSIZE = 3'b000; // byte + + + if(t.trans == t.WRITE) begin + case(HSIZE) + 3'b000: mem.write_byte(t.addr, t.wdata); //byte + 3'b001: mem.write_half(t.addr, t.wdata); //half word + 3'b010: mem.write_word(t.addr, t.wdata); //word + endcase + + // $info("PREDICTOR DEBUG write addr: %h value: %h size: %h",t.addr, t.wdata, HSIZE); + + // $display("predictor"); + // $display("predictor mem"); + // mem.print_all(); + // $display(""); + end + endfunction +endclass //predictor + + + +class comparator extends uvm_scoreboard; + `uvm_component_utils(comparator) + uvm_analysis_export #(transaction) response_export; + uvm_tlm_analysis_fifo #(transaction) response_fifo; + sim_mem mem; + logic [2:0] HSIZE; + transaction response; + logic [31:0] expected; + transactionSeq tx_seq; + + int m_matches, m_mismatches; // records number of matches and mismatches + + function new( string name , uvm_component parent) ; + super.new( name , parent ); + m_matches = 0; + m_mismatches = 0; + endfunction + + function void connect_phase(uvm_phase phase); + response_export.connect(response_fifo.analysis_export); + endfunction + + function void build_phase( uvm_phase phase ); + response_export = new("response_export", this); + response_fifo = new("response_fifo", this); + endfunction + + task run_phase(uvm_phase phase); + int error_tx_time; //time that error write transaction happens + forever begin + response_fifo.get(response); + if(response.byte_en == 4'b1111) + HSIZE = 3'b010; // word + else if(response.byte_en == 4'b1100 || response.byte_en == 4'b0011) + HSIZE = 3'b001; // half word + else + HSIZE = 3'b000; // byte + + case(HSIZE) + 3'b000: expected = mem.read_byte(response.addr); //byte + 3'b001: expected = mem.read_half(response.addr); //half word + 3'b010: expected = mem.read_word(response.addr); //word + endcase + + if(expected == response.wdata) begin + m_matches++; + uvm_report_info("Comparator", "Data Match"); + end else begin + m_mismatches++; + uvm_report_error("Comparator", "Error: Data Mismatch"); + + $display("comparator"); + $info("response: %h", response.wdata); + $display("expected addr:%h value: %h size: %h byte_en: %b", response.addr , expected, HSIZE, response.byte_en); + + error_tx_time = tx_seq.search_time(response.addr); + $info("error transaction happens at %d", error_tx_time); + end + end + endtask + + function void report_phase(uvm_phase phase); + uvm_report_info("Comparator", $sformatf("Matches: %0d", m_matches)); + uvm_report_info("Comparator", $sformatf("Mismatches: %0d", m_mismatches)); + endfunction + +endclass //comparator + +class ahb_env extends uvm_env; + `uvm_component_utils(ahb_env) + ahb_agent agt; + comparator comp; // scoreboard + predictor pred; + sim_mem mem; + transactionSeq tx_seq; //to record and lookup transactions + + function new(string name = "env", uvm_component parent = null); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + agt = ahb_agent::type_id::create("agt", this); + comp = comparator::type_id::create("comp", this); + pred = predictor::type_id::create("pred", this); + tx_seq = new(); + mem = new(); + if(!mem.randomize()) begin + `uvm_fatal("environment", "not able to randomize mem") + end + pred.mem = mem; + comp.mem = mem; + uvm_config_db#(sim_mem)::set( null, "", "slave_mem", mem); + pred.tx_seq = tx_seq; + comp.tx_seq = tx_seq; + endfunction + + function void connect_phase(uvm_phase phase); + agt.cpu.cpu_ap.connect(pred.analysis_export); + agt.cpu.response_ap.connect(comp.response_export); + endfunction + +endclass //ahb_env + + + +class ahb_test extends uvm_test; + `uvm_component_utils(ahb_test) + + ahb_env env; + ahb_seq seq; + + function new(string name = "ahb_test", uvm_component parent); + super.new(name, parent); + endfunction: new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + env = ahb_env::type_id::create("env",this); + seq = ahb_seq::type_id::create("seq"); + endfunction: build_phase + + task run_phase(uvm_phase phase); + phase.raise_objection( this, "Starting sequence in main phase" ); + $display("%t Starting sequence run_phase",$time); + + seq.start(env.agt.sqr); + #100ns; + phase.drop_objection( this , "Finished in main phase" ); + endtask + +endclass //ahb_test + From 47abfb456f8d17f5db0533ddbe8f4d223423b620 Mon Sep 17 00:00:00 2001 From: Cole Nelson <35197931+cole-nelson@users.noreply.github.com> Date: Sun, 25 Oct 2020 22:40:06 -0400 Subject: [PATCH 05/47] Mul div fix (#4) Implementation of a 3-stage pipelined multiplier and a radix-4 restoring divider. Passes all RV32M tests, and are synthesizable. Co-authored-by: Jing Yin See Co-authored-by: Yuqing Fan --- .../extensions/rv32m/carry_save_adder.sv | 21 ++ .../extensions/rv32m/flex_counter_mul.sv | 51 ++++ .../risc_mgmt/extensions/rv32m/full_adder.sv | 12 + .../risc_mgmt/extensions/rv32m/pp_mul32.sv | 242 ++++++++++++++++++ .../extensions/rv32m/radix4_divider.sv | 140 ++++++++++ .../extensions/rv32m/rv32m_execute.sv | 4 +- source_code/tb/tb_div.sv | 83 ++++++ source_code/tb/tb_pp_mul32.sv | 117 +++++++++ source_code/tb/tb_radix4_divider.sv | 105 ++++++++ 9 files changed, 773 insertions(+), 2 deletions(-) create mode 100644 source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv create mode 100644 source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv create mode 100644 source_code/risc_mgmt/extensions/rv32m/full_adder.sv create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv create mode 100644 source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv create mode 100644 source_code/tb/tb_div.sv create mode 100644 source_code/tb/tb_pp_mul32.sv create mode 100644 source_code/tb/tb_radix4_divider.sv diff --git a/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv b/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv new file mode 100644 index 000000000..b3a0277d1 --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv @@ -0,0 +1,21 @@ +module carry_save_adder +#( + parameter BIT_WIDTH = 32 +) +( + input logic [(BIT_WIDTH-1):0] x, + input logic [(BIT_WIDTH-1):0] y, + input logic [(BIT_WIDTH-1):0] z, + output logic [(BIT_WIDTH-1):0] cout, + output logic [(BIT_WIDTH-1):0] sum +); + genvar i; + logic [(BIT_WIDTH-1):0] c; + generate + for (i = 0; i < BIT_WIDTH; i = i + 1) + begin + full_adder FA (.x(x[i]), .y(y[i]), .cin(z[i]), .cout(c[i]), .sum(sum[i])); + end + endgenerate + assign cout = c << 1; +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv b/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv new file mode 100644 index 000000000..946514ea1 --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv @@ -0,0 +1,51 @@ +module flex_counter_mul +# ( + parameter NUM_CNT_BITS = 4 +) +( + input wire clk, + input wire n_rst, + input wire clear, + input wire count_enable, + input reg [(NUM_CNT_BITS-1):0] rollover_val, + output reg [(NUM_CNT_BITS-1):0] count_out, + output reg rollover_flag +); + + reg [(NUM_CNT_BITS-1):0] next_count; + reg next_flag; + always_ff @ (posedge clk, negedge n_rst) + begin + if (n_rst == 0) + begin + count_out <= '0; + rollover_flag <= 1'b0; + end else begin + count_out <= next_count; + rollover_flag <= next_flag; + end + end + + always_comb + begin + if (clear == 1) + next_flag = 0; + else if (count_enable == 0) + next_flag = rollover_flag; + else if (count_out == (rollover_val - 1) & clear == 1'b0) + next_flag = 1; + else + next_flag = 0; + + + if(clear) + next_count = 0; + else if(count_enable) begin + if(count_out == rollover_val) + next_count = 1; + else + next_count = count_out + 1; + end else + next_count = count_out; + end +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/full_adder.sv b/source_code/risc_mgmt/extensions/rv32m/full_adder.sv new file mode 100644 index 000000000..96e6b6cbc --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/full_adder.sv @@ -0,0 +1,12 @@ +module full_adder +( + input logic x, + input logic y, + input logic cin, + output logic cout, + output logic sum +); + assign sum = x ^ y ^ cin; + assign cout = (x & y) | (x & cin) | (y & cin); + +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv b/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv new file mode 100644 index 000000000..3417cf8bb --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv @@ -0,0 +1,242 @@ +// Pipelined multiplier - 32 bits +module pp_mul32 +( + input logic CLK, + input logic nRST, + input logic [31:0] multiplicand, + input logic [31:0] multiplier, + input logic [1:0] is_signed, + input logic start, + output logic finished, + output logic [63:0] product +); + //logic start_reg; + logic [31:0] multiplicand_reg; + logic [31:0] multiplier_reg; + logic [63:0] result; + logic [63:0] result2; + logic [63:0] temp_product; + logic [63:0] temp_product2; + logic [31:0] multiplicand_mod; + logic [31:0] multiplier_mod; + logic adjust_product; + logic [63:0] partial_product [15:0]; + logic [63:0] pp0, pp1, pp2, pp3, pp4, pp5, pp6, pp7, pp8, pp9, pp10, pp11, pp12, pp13, pp14, pp15; + logic [32:0] mul_plus2, mul_minus2, mul_minus1; + logic [63:0] pp [15:0]; + logic [32:0] modified_in; + logic [63:0] sum0, sum1, sum2, sum3, sum4, sum5, sum6, sum7, sum8, sum9, sum10, sum11, sum12, sum13; + logic [63:0] cout0, cout1, cout2, cout3, cout4, cout5, cout6, cout7, cout8, cout9, cout10, cout11, cout12, cout13; + logic [1:0] count; + logic mult_complete; + //logic [63:0] sum13_pip, cout13_pip; + logic [63:0] sum5_pip, cout5_pip, sum6_pip, cout6_pip, sum7_pip, cout7_pip; + logic [1:0] is_signed_reg; + logic done; + logic count_ena; + integer i, j; + + always_ff @ (posedge CLK, negedge nRST) begin + if (nRST == 0) begin + multiplicand_reg <= '0; + multiplier_reg <= '0; + is_signed_reg <= '0; + end + else if (start) begin + multiplicand_reg <= multiplicand; + multiplier_reg <= multiplier; + is_signed_reg <= is_signed; + end + end + // Modify multiplicand and multiplier if they are signed + assign multiplicand_mod = is_signed_reg[1] && multiplicand_reg[31] ? (~(multiplicand_reg)+1) : multiplicand_reg; + assign multiplier_mod = is_signed_reg[0] && multiplier_reg[31] ? (~(multiplier_reg)+1) : multiplier_reg; + // Control signal to modify final product + assign adjust_product = (is_signed_reg[0] & multiplier_reg[31]) ^ (is_signed_reg[1] & multiplicand_reg[31]); + // For bit pair recoding part + assign mul_plus2 = multiplicand_mod + multiplicand_mod; + assign mul_minus2 = ~mul_plus2 + 1; + assign mul_minus1 = ~multiplicand_mod + 1; + assign modified_in = {multiplier_mod,1'b0}; + + // STAGE 1: BOOTH ENCODER + // Bit pair recoding to generate partial product + always_comb begin + for (i = 0; i < 32; i = i + 2) begin + case ({modified_in[i+2], modified_in[i+1], modified_in[i]}) + 3'b000: + pp[i/2] = '0; //0 + 3'b001: + pp[i/2] = {{32'd0},multiplicand_mod}; // +1M + 3'b010: + pp[i/2] = {{32'd0},multiplicand_mod}; // +1M + 3'b011: + pp[i/2] = {{31'd0},mul_plus2}; // +2M + 3'b100: + if (mul_minus2 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}},mul_minus2}; // -2M + 3'b101: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}},mul_minus1}; // -1M + 3'b110: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}},mul_minus1}; // -1M + 3'b111: + pp[i/2] = '0; + endcase + end + end + // Shift partial product + always_comb begin + for (j = 0; j < 16; j = j + 1) + begin + partial_product[j] = pp[j] << (2*j); // Shift with multiple of 2 (Radix 4) + end + end + + // Pipeline register before wallace tree + always_ff @ (posedge CLK, negedge nRST) begin + if (nRST == 0) begin + pp0 <= '0; + pp1 <= '0; + pp2 <= '0; + pp3 <= '0; + pp4 <= '0; + pp5 <= '0; + pp6 <= '0; + pp7 <= '0; + pp8 <= '0; + pp9 <= '0; + pp10 <= '0; + pp11 <= '0; + pp12 <= '0; + pp13 <= '0; + pp14 <= '0; + pp15 <= '0; + end + else begin + pp0 <= partial_product[0]; + pp1 <= partial_product[1]; + pp2 <= partial_product[2]; + pp3 <= partial_product[3]; + pp4 <= partial_product[4]; + pp5 <= partial_product[5]; + pp6 <= partial_product[6]; + pp7 <= partial_product[7]; + pp8 <= partial_product[8]; + pp9 <= partial_product[9]; + pp10 <= partial_product[10]; + pp11 <= partial_product[11]; + pp12 <= partial_product[12]; + pp13 <= partial_product[13]; + pp14 <= partial_product[14]; + pp15 <= partial_product[15]; + end + end + + // STAGE 2: WALLACE TREE + // Layer 1 + carry_save_adder #(64) CSA0 (.x(pp0), .y(pp1), .z(pp2), .cout(cout0), .sum(sum0)); + carry_save_adder #(64) CSA1 (.x(pp3), .y(pp4), .z(pp5), .cout(cout1), .sum(sum1)); + carry_save_adder #(64) CSA2 (.x(pp6), .y(pp7), .z(pp8), .cout(cout2), .sum(sum2)); + carry_save_adder #(64) CSA3 (.x(pp9), .y(pp10), .z(pp11), .cout(cout3), .sum(sum3)); + carry_save_adder #(64) CSA4 (.x(pp12), .y(pp13), .z(pp14), .cout(cout4), .sum(sum4)); // remaining partialproduct 15 + // Layer 2 + carry_save_adder #(64) CSA5 (.x(cout0), .y(sum0), .z(cout1), .cout(cout5), .sum(sum5)); + carry_save_adder #(64) CSA6 (.x(sum1), .y(cout2), .z(sum2), .cout(cout6), .sum(sum6)); + carry_save_adder #(64) CSA7 (.x(cout3), .y(sum3), .z(cout4), .cout(cout7), .sum(sum7)); // remaining sum4 + // Pipeline register in wallace tree between layer 2 and layer 3 + always_ff @ (posedge CLK, negedge nRST) begin + if (nRST == 0) begin + cout5_pip <= '0; + sum5_pip <= '0; + cout6_pip <= '0; + sum6_pip <= '0; + cout7_pip <= '0; + sum7_pip <= '0; + end + else begin + cout5_pip <= cout5; + sum5_pip <= sum5; + cout6_pip <= cout6; + sum6_pip <= sum6; + cout7_pip <= cout7; + sum7_pip <= sum7; + end + end + + // Layer 3 + carry_save_adder #(64) CSA8 (.x(cout5), .y(sum5), .z(cout6), .cout(cout8), .sum(sum8)); + carry_save_adder #(64) CSA9 (.x(sum6), .y(cout7), .z(sum7), .cout(cout9), .sum(sum9)); + // Layer 4 + carry_save_adder #(64) CSA10 (.x(cout8), .y(sum8), .z(cout9), .cout(cout10), .sum(sum10)); + carry_save_adder #(64) CSA11 (.x(sum9), .y(pp15), .z(sum4), .cout(cout11), .sum(sum11)); + // Layer 5 + carry_save_adder #(64) CSA12 (.x(cout10), .y(sum10), .z(cout11), .cout(cout12), .sum(sum12)); // remaining sum11 + // Layer 6 + carry_save_adder #(64) CSA13 (.x(cout12), .y(sum12), .z(sum11), .cout(cout13), .sum(sum13)); + + // STAGE 3: NORMAL ADDER + flex_counter_mul #(2) FC (.clk(CLK), .n_rst(nRST), .clear(start), .count_enable(count_ena), .rollover_val(2'd2), .count_out(count), .rollover_flag(finished)); + assign temp_product = cout13 + sum13; + assign temp_product2 = is_signed_reg[0] == 0 && multiplier_reg[31] ? temp_product + ({{33{multiplicand_mod[31]}},multiplicand_mod} << 32) : temp_product; // plus extra 1M + assign result = adjust_product ? (~temp_product2)+1: temp_product2; + assign mult_complete = count == 2'd1 | count == 2'd2; + assign result2 = mult_complete ? result: '0; + + always_ff @ (posedge CLK, negedge nRST) begin + if (nRST == 0) begin + product <= '0; + end + else begin + product <= result2; + end + end + + //Small FSM to control flex counter + typedef enum logic {IDLE, START} state_type; + state_type state, next_state; + always_ff @ (posedge CLK, negedge nRST) begin + if (nRST == 0) + state <= IDLE; + else + state <= next_state; + end + + always_comb begin + /* + next_state = state; + case (state) + IDLE: begin + if (start) + next_state = START; + end + START: begin + if (finished) + next_state = IDLE; + end + endcase + */ + next_state = state; + if(state == IDLE && start) begin + next_state = START; + end else if(state == START && finished) begin + next_state = IDLE; + end else begin + next_state = state; + end + end + + always_comb begin + count_ena = 0; + case (state) + IDLE: begin + count_ena = 0; + end + START: begin + count_ena = ~finished; + end + endcase + end + +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv b/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv new file mode 100644 index 000000000..6665a1e9f --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv @@ -0,0 +1,140 @@ +module radix4_divider +#( + parameter NUM_BITS = 32 +) +( + input logic CLK, + input logic nRST, + input logic start, + input logic is_signed, //new + input logic [NUM_BITS-1:0] dividend, + input logic [NUM_BITS-1:0] divisor, + output logic [NUM_BITS-1:0] quotient, + output logic [NUM_BITS-1:0] remainder, + output logic finished + +); + logic [NUM_BITS-1:0] next_remainder, next_quotient, shifted_remainder, shifted_quotient, temp_quotient, temp_remainder; + logic [NUM_BITS:0] Result1, Result2, Result3; + logic [NUM_BITS-1:0] DivisorX2, DivisorX3; + logic [4:0] count, next_count; + + logic [NUM_BITS-1:0] usign_divisor, usign_dividend; + logic adjustment_possible, adjust_quotient, adjust_remainder; + logic div_done; + + assign usign_divisor = is_signed & divisor[NUM_BITS-1] ? (~divisor)+1 : divisor; + assign usign_dividend = is_signed & dividend[NUM_BITS-1] ? (~dividend)+1 : dividend; + assign adjustment_possible = is_signed && (divisor[NUM_BITS-1] ^ dividend[NUM_BITS-1]); + assign adjust_quotient = adjustment_possible && ~quotient[NUM_BITS-1]; + assign adjust_remainder = is_signed && dividend[NUM_BITS-1]; + assign div_done = (count == 0); + assign quotient = temp_quotient; + assign remainder = temp_remainder; + +/* + always_comb begin + quotient = temp_quotient; + remainder = temp_remainder; + if (count == 5'b1) begin + quotient = adjust_quotient ? ~temp_quotient + 1 : temp_quotient; + remainder = adjust_remainder ? ~temp_remainder + 1 : temp_remainder; + end + end +*/ +/* + always_ff @(posedge CLK, negedge nRST) begin + if (~finished && adjust_quotient) + quotient <= ~quotient + 1; + + else if(~finished && adjust_remainder ) + remainder <= ~remainder + 1; + + else begin + quotient <= quotient; + remainder <= remainder; + end + end +*/ + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + finished <= 1'b0; + end else if(start) begin + finished <= 1'b0; + end else if(div_done) begin + finished <= 1'b1; + end + end + //initialize d2 d3 + assign DivisorX2 = usign_divisor << 1; //Divisor*2 + assign DivisorX3 = (usign_divisor << 1) + usign_divisor; //Divisor*3 + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + + count <= 5'd16; + temp_quotient <= '0; + temp_remainder <= '0; + end else if (start) begin + temp_quotient <= usign_dividend; + temp_remainder <= '0; + count <= 5'd16; + + end else if (count >= 0) begin + temp_quotient <= next_quotient; + temp_remainder <= next_remainder; + count <= next_count; + end + end + + always_comb begin + + next_quotient = temp_quotient; + next_remainder = temp_remainder; + next_count = count; + + if (count != 0) begin + next_count = count - 1; + shifted_remainder = (temp_remainder << 2) | temp_quotient[NUM_BITS-1:NUM_BITS-2]; + shifted_quotient = temp_quotient << 2; + Result1 = shifted_remainder - usign_divisor; + Result2 = shifted_remainder - DivisorX2; + Result3 = shifted_remainder - DivisorX3; + if(Result1[NUM_BITS-1] | Result1[NUM_BITS]) begin + next_remainder = shifted_remainder; + next_quotient = shifted_quotient | 0; + if (count == 1 && adjust_quotient ) + next_quotient = ~next_quotient + 1; + + if(count == 1 && adjust_remainder ) + next_remainder = ~next_remainder + 1; + + end else if(Result2[NUM_BITS-1] | Result2[NUM_BITS]) begin + next_remainder = Result1; + next_quotient = shifted_quotient | 1; + if (count == 1 && adjust_quotient ) + next_quotient = ~next_quotient + 1; + + if(count == 1 && adjust_remainder ) + next_remainder = ~next_remainder + 1; + end else if(Result3[NUM_BITS-1] | Result3[NUM_BITS]) begin + next_remainder = Result2; + next_quotient = shifted_quotient | 2; + if (count == 1 && adjust_quotient ) + next_quotient = ~next_quotient + 1; + + if(count == 1 && adjust_remainder ) + next_remainder = ~next_remainder + 1; + end else begin + next_remainder = Result3; + next_quotient = shifted_quotient | 3; + if (count == 1 && adjust_quotient ) + next_quotient = ~next_quotient + 1; + + if(count == 1 && adjust_remainder ) + next_remainder = ~next_remainder + 1; + end + end + + end +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv b/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv index 8a6308f50..edc4ee8d6 100644 --- a/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv +++ b/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv @@ -91,7 +91,7 @@ module rv32m_execute ( assign mul_start = operand_diff && operation[2]; // Module instantiations - shift_add_multiplier #(.N(WORD_SIZE)) mult_i ( + pp_mul32 mult_i ( .CLK(CLK), .nRST(nRST), .multiplicand(multiplicand), @@ -116,7 +116,7 @@ module rv32m_execute ( assign div_zero = (divisor == 32'h0); assign div_start = operand_diff && ~operation[2] & ~overflow & ~div_zero; - shift_test_restore_divider #(.N(WORD_SIZE)) div_i ( + radix4_divider div_i( .CLK(CLK), .nRST(nRST), .divisor(divisor), diff --git a/source_code/tb/tb_div.sv b/source_code/tb/tb_div.sv new file mode 100644 index 000000000..1db20c78e --- /dev/null +++ b/source_code/tb/tb_div.sv @@ -0,0 +1,83 @@ +module tb_div (); + + parameter PERIOD = 20; + + /* Signal Instantiations */ + logic CLK, nRST; + logic [31:0] divisor; + logic [31:0] dividend; + logic is_signed; + logic start, finished; + logic [31:0] remainder, quotient; + logic [31:0] test_num; + + /* Module Instantiations */ + shift_test_restore_divider #(32) DUT (.*); + + /* CLK generation */ + + initial begin + CLK = 0; + end + + always begin + CLK = ~CLK; + #(PERIOD/2); + end + + + task test_div( + input logic [31:0] a, + input logic [31:0] b, + input logic is_signed_t, + input logic [31:0] exp_quot,exp_rem +); +divisor = a; +dividend = b; +is_signed = is_signed_t; +start = 1; +@(posedge CLK); +@(posedge CLK); +#(9) +start=0; +while(finished==0) +@(posedge CLK); + +assert (quotient == exp_quot) else $error ("Division failed for test %0d: Expected %h Received %h\n",test_num, exp_quot, quotient); + +assert (remainder == exp_rem) else $error ("Remainder failed for test %0d: Expected %h Received %h\n",test_num, exp_rem, remainder); + +test_num++; +endtask + + +initial begin +// reset +test_num = 0; +nRST = 1'b0; +divisor = 0; +dividend = 0; +is_signed = 0; +start = 0; +@(posedge CLK); +@(posedge CLK); +#(PERIOD/2); +nRST = 1'b1; +@(posedge CLK); +@(posedge CLK); +@(posedge CLK); + +// 80/2 unsigned = 40 .... 0 +test_div(32'd8,32'd20,1'd0,32'd2, 32'h4); + +test_div(32'h7,32'hFFFFFFF7,1'h1,32'hFFFFFFFE, 32'h2);//-9 / 7 = -1...-2 + + +$finish; +end +endmodule + + + + + diff --git a/source_code/tb/tb_pp_mul32.sv b/source_code/tb/tb_pp_mul32.sv new file mode 100644 index 000000000..4aff3a1b1 --- /dev/null +++ b/source_code/tb/tb_pp_mul32.sv @@ -0,0 +1,117 @@ +`timescale 1ns/10ps +module tb_pp_mul32 (); + parameter BIT_WIDTH = 32; + parameter CLOCK_PERIOD = 10ns; + logic tb_CLK, tb_nRST; + logic tb_start, tb_finished; + logic [(BIT_WIDTH-1):0] tb_multiplicand; + logic [(BIT_WIDTH-1):0] tb_multiplier; + logic [(2*BIT_WIDTH-1):0] tb_product; + logic [1:0] tb_is_signed; + logic [(2*BIT_WIDTH+5):0] tb_expected_out; + integer tb_test_case_num; + typedef struct { + string test_name; + logic [(BIT_WIDTH-1):0] test_multiplicand; + logic [(BIT_WIDTH-1):0] test_multiplier; + logic [1:0] test_is_signed; + } testvector; + testvector tb_test_case []; + + pp_mul32 DUT (.CLK(tb_CLK), .nRST(tb_nRST), .multiplicand(tb_multiplicand), .multiplier(tb_multiplier), .is_signed(tb_is_signed), .start(tb_start), .finished(tb_finished), .product(tb_product)); + + always begin + tb_CLK=0; + #(CLOCK_PERIOD/2.0); + tb_CLK=1; + #(CLOCK_PERIOD/2.0); + end + + task reset_dut(); + @(negedge tb_CLK); + tb_nRST = 0; + @(posedge tb_CLK); + @(posedge tb_CLK); + #(CLOCK_PERIOD/4.0); + tb_nRST = 1; + endtask + + initial begin + tb_test_case = new[9]; + // Random multiplier and multiplicand + tb_test_case[0].test_name = "Random multiplier and multiplicand"; + tb_test_case[0].test_multiplicand = 32'd183978223; + tb_test_case[0].test_multiplier = 32'd490177653; + tb_test_case[0].test_is_signed = 2'b00; + // Multiplier with concatenation of all possible 3-bits values in bitpair recoding + tb_test_case[1].test_name = "Multiplier with concatenation of all possible 3-bits values in bitpair recoding"; + tb_test_case[1].test_multiplicand = 32'd478013; + tb_test_case[1].test_multiplier = {{10'd0}, {22'b1110100110011100100100}}; + tb_test_case[1].test_is_signed = 2'b00; + // Unsigned multiplicand and unsigned multiplier + tb_test_case[2].test_name = "Unsigned multiplicand and unsigned multiplier"; + tb_test_case[2].test_multiplicand = '1 >> 1; + tb_test_case[2].test_multiplier = '1 >> 1; + tb_test_case[2].test_is_signed = 2'b00; + // Signed multiplicand and unsigned multiplier + tb_test_case[3].test_name = "Signed multiplicand and unsigned multiplier"; + tb_test_case[3].test_multiplicand = -28752; + tb_test_case[3].test_multiplier = 32'd839011; + tb_test_case[3].test_is_signed = 2'b10; + // Unsigned multiplicand and signed multiplier + tb_test_case[4].test_name = "Unsigned multiplicand and signed multiplier"; + tb_test_case[4].test_multiplicand = 32'd7212691; + tb_test_case[4].test_multiplier = -43892; + tb_test_case[4].test_is_signed = 2'b01; + // Signed multiplicand and signed multiplier + tb_test_case[5].test_name = "Signed multiplicand and signed multiplier"; + tb_test_case[5].test_multiplicand = -7268; + tb_test_case[5].test_multiplier = -897192; + tb_test_case[5].test_is_signed = 2'b11; + // MSB is 1 but unsigned - Both + tb_test_case[6].test_name = "MSB is 1 but unsigned - Both"; + tb_test_case[6].test_multiplicand = '1; + tb_test_case[6].test_multiplier = '1; + tb_test_case[6].test_is_signed = 2'b00; + // MSB is 1 but unsigned - Multiplicand + tb_test_case[7].test_name = "MSB is 1 but unsigned - Multiplicand"; + tb_test_case[7].test_multiplicand = '1; + tb_test_case[7].test_multiplier = 32'd59; + tb_test_case[7].test_is_signed = 2'b00; + // MSB is 1 but unsigned - Multiplier + tb_test_case[8].test_name = "MSB is 1 but unsigned - Multiplier"; + tb_test_case[8].test_multiplicand = 32'd38013; + tb_test_case[8].test_multiplier = '1; + tb_test_case[8].test_is_signed = 2'b00; + end + initial begin + tb_multiplier = '0; + tb_multiplicand = '0; + tb_is_signed = '0; + tb_start = 0; + tb_nRST = 1; + for (tb_test_case_num = 0; tb_test_case_num < tb_test_case.size(); tb_test_case_num ++) begin + $display("TEST CASE %d - %s", tb_test_case_num, tb_test_case[tb_test_case_num].test_name); + reset_dut(); + @(posedge tb_CLK); + #(CLOCK_PERIOD/4.0); + tb_multiplicand = tb_test_case[tb_test_case_num].test_multiplicand; + tb_multiplier = tb_test_case[tb_test_case_num].test_multiplier; + tb_is_signed = tb_test_case[tb_test_case_num].test_is_signed; + @(negedge tb_CLK); + tb_start = 1; + @(negedge tb_CLK); // First Clock Cycle + tb_start = 0; + tb_expected_out = tb_multiplicand * tb_multiplier; + @(posedge tb_CLK); // Second Clock Cycle + @(posedge tb_CLK); // Third Clock Cycle + #(CLOCK_PERIOD/4.0); + assert (tb_product == tb_expected_out) + $info ("CORRECT MULTIPLICATION"); + else + $error ("ACTUAL: %d, EXPECTED: %d", tb_product, tb_expected_out); // see waveform value for test case 3-5 (waveform will show negative value) + end + $finish; + end + +endmodule diff --git a/source_code/tb/tb_radix4_divider.sv b/source_code/tb/tb_radix4_divider.sv new file mode 100644 index 000000000..c3dcb374c --- /dev/null +++ b/source_code/tb/tb_radix4_divider.sv @@ -0,0 +1,105 @@ +`timescale 1ns/10ps +module tb_radix4_divider (); + + parameter PERIOD = 10; + + /* Signal Instantiations */ + logic CLK, nRST; + logic [31:0] divisor; + logic [31:0] dividend; + logic is_signed; + logic start, finished; + logic [31:0] remainder, quotient; + logic [31:0] test_num; + + /* Module Instantiations */ + radix4_divider #(32) DUT + ( + .CLK(CLK), + .nRST(nRST), + .start(start), + .dividend(dividend), + .divisor(divisor), + .quotient(quotient), + .remainder(remainder) + ); + /* CLK generation */ + + initial begin + CLK = 0; + end + + always begin + CLK = ~CLK; + #(PERIOD/2); + end + + task wait_result(); + for (int i = 0; i < 20; i++) + @(posedge CLK); + endtask + + task test_div( + input logic [31:0] a, + input logic [31:0] b, + input logic is_signed_t, + input logic [31:0] exp_quot, exp_rem + ); + divisor = a; + dividend = b; + is_signed = is_signed_t; + start = 1; + @(posedge CLK); + @(posedge CLK); + #(9); + start = 0; + while(!finished) + @(posedge CLK); + + assert (quotient == exp_quot) else $error("Division failed for test %0d: Expected %h Received %h\n", test_num, exp_quot, quotient); + assert (remainder == exp_rem) else $error("Remainder failed for test %0d: Expected %h Received %h\n", test_num, exp_rem, remainder); + test_num++; + endtask + + /* Begin Testing */ + + initial begin + // reset divider + test_num = 0; + nRST = 1'b0; + divisor = 0; + dividend = 0; + is_signed = 0; + start = 0; + @(posedge CLK); + @(posedge CLK); + #(PERIOD/2); + nRST = 1'b1; + @(posedge CLK); + @(posedge CLK); + @(posedge CLK); + + @(negedge CLK); + divisor = 32'd6; + dividend = 32'd18; + start = 1'b1; + #(PERIOD); + start = 1'b0; + wait_result(); + + @(negedge CLK); + divisor = 32'd23910; + dividend = 32'd8212148; + start = 1'b1; + #(PERIOD); + start = 1'b0; + wait_result(); + + //Basic test divisor, dividend, signed, quot, rem + //test_div(32'h80, 32'h2, 1'b0, 32'h40, 32'h0); + + + $finish; + end + +endmodule From 045c64e0611bd55d1f262520400d2afa71c0f06f Mon Sep 17 00:00:00 2001 From: Cole Nelson <35197931+cole-nelson@users.noreply.github.com> Date: Tue, 9 Mar 2021 16:20:19 -0500 Subject: [PATCH 06/47] Priv Unit Fixes (#5) * adjustments to priv control to account for interrupt mie issues * Added commentary on the PRIV_CONTROL fix * Fixing prior commit -- added the wrong files. Reverting changes & adding correct files * Fixed memory controller bug where interrupts caused FSM to lock up. Passes all RVB tests, and works in AFTx06 integration. * Added halt on infinite loop as microarch paramter, fixed minor memory controller bug * Fixed priv unit issue where mepc was captured during interrupt handler Co-authored-by: Christopher Chiminski --- example.yml | 3 ++ scripts/config_core.py | 4 +- .../privs/priv_1_11/priv_1_11_control.sv | 41 +++++++++++++++---- source_code/standard_core/control_unit.sv | 11 ++++- .../standard_core/memory_controller.sv | 26 ++++++++++-- sparce.yml | 3 ++ 6 files changed, 74 insertions(+), 14 deletions(-) diff --git a/example.yml b/example.yml index af4c4da92..85c79f5d2 100644 --- a/example.yml +++ b/example.yml @@ -19,6 +19,9 @@ microarch_params: # Sparisty Optimizations sparce_enabled : "disabled" + # Halt + infinite_loop_halts: "true" + # RISC-MGMT Extension Configuration risc_mgmt_params: standard_extensions: diff --git a/scripts/config_core.py b/scripts/config_core.py index d85e42a96..b75d3c24a 100755 --- a/scripts/config_core.py +++ b/scripts/config_core.py @@ -46,7 +46,9 @@ 'bus_endianness' : ['big', 'little'], 'bus_interface_type' : ['ahb_if', 'generic_bus_if'], # Sparisty Optimizations - 'sparce_enabled' : [ 'enabled', 'disabled' ] + 'sparce_enabled' : [ 'enabled', 'disabled' ], + # Halt Enable -- Good for testing, not for tapeout + 'infinite_loop_halts' : ['true', 'false'] } RISC_MGMT_PARAMS = \ diff --git a/source_code/privs/priv_1_11/priv_1_11_control.sv b/source_code/privs/priv_1_11/priv_1_11_control.sv index 002d3e84f..47aed5a7b 100644 --- a/source_code/privs/priv_1_11/priv_1_11_control.sv +++ b/source_code/privs/priv_1_11/priv_1_11_control.sv @@ -36,7 +36,7 @@ module priv_1_11_control ( int_code_t intr_src; logic interrupt, clear_interrupt; - logic interrupt_reg, interrupt_fired; + logic interrupt_reg, interrupt_fired, update_mie; always_comb begin // determine the source of the interrupt to be stored in the mcause register interrupt = 1'b1; @@ -155,8 +155,8 @@ module priv_1_11_control ( always_comb begin prv_intern_if.mstatus_next.mie = prv_intern_if.mstatus.mie; prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mpie; - - if (prv_intern_if.intr) begin // interrupt has truly been registered and it is time to go to the vector table + //changed from intr + if (update_mie) begin // interrupt has truly been registered and it is time to go to the vector table prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mie; // when a trap is taken mpie is set to the current mie prv_intern_if.mstatus_next.mie = 1'b0; // disable the interrupt once it enters the handler @@ -167,8 +167,13 @@ module priv_1_11_control ( end - // Update EPC as soon as interrupt or exception is found - assign prv_intern_if.mepc_rup = exception | interrupt; // TODO: Change to interrupt + // Update EPC as soon as interrupt or exception is found + // Note: mepc cannot update immediately, as if the processor is in an interrupt already, + // the MEPC captured will be within the interrupt (and nested interrupts are not supported). + // Interrupt fired notes when an interrupt is seen by the processor, i.e. when mstatus.mie is high again. + // The signal is 2 cycles long, so the update_mie signal is used to clip it down to 1 to prevent MEPC + // double update which results in skipping an instruction. + assign prv_intern_if.mepc_rup = exception | (interrupt_fired & ~update_mie); // TODO: Change to interrupt assign prv_intern_if.mepc_next = prv_intern_if.epc; assign prv_intern_if.mtval_rup = (prv_intern_if.mal_l | prv_intern_if.fault_l | prv_intern_if.mal_s | prv_intern_if.fault_s | @@ -183,10 +188,32 @@ module priv_1_11_control ( always_ff @ (posedge CLK, negedge nRST) begin if (!nRST) interrupt_reg <= '0; - else if (interrupt_fired) + else if (interrupt_fired) interrupt_reg <= 1'b1; else if (prv_intern_if.pipe_clear) - interrupt_reg <= '0; + interrupt_reg <= '0; + end + + /* + * Fix for MIE/MPIE issue. This used to be the same as 'interrupt_reg' above, + * but the above stays high for 2+ cycles (i.e. waiting for pipe_clear). + * This caused MPIE to update twice; the first update would set MPIE to 1, + * and the second would cause MPIE to return to 0. Then, after an MRET, + * MIE would not be restored since MPIE was lost. Additionally, shortening + * interrupt_reg was not an option since pipe_clear must be asserted for the + * PC to be inserted into the pipeline from the priv unit, so creating this + * extra register was the cleanest solution to ensuring MPIE updates exactly + * once. + */ + always_ff @(posedge CLK, negedge nRST) begin + if(!nRST) + update_mie <= '0; + else if(interrupt_fired && ~update_mie) + update_mie <= 1'b1; + else if (prv_intern_if.pipe_clear) + update_mie <= '0; + else + update_mie <= '0; end endmodule diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index db1b7152c..7aa56033f 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -185,8 +185,15 @@ module control_unit end // HALT HACK. Just looking for j + 0x0 (infinite loop) - // TODO: FIX ME WHEN IMPLEMENTING INTERRUPTS - assign cu_if.halt = (cu_if.instr == 32'h0000006f); + // Halt required for unit testing, but not useful in tapeout context + // Due to presence of interrupts, infinite loops are valid + generate + if(INFINITE_LOOP_HALTS == "true") begin + assign cu_if.halt = (cu_if.instr == 32'h0000006f); + end else begin + assign cu_if.halt = '0; + end + endgenerate // Privilege Control Signals assign cu_if.fault_insn = '0; diff --git a/source_code/standard_core/memory_controller.sv b/source_code/standard_core/memory_controller.sv index cd850d0e8..686fbd576 100644 --- a/source_code/standard_core/memory_controller.sv +++ b/source_code/standard_core/memory_controller.sv @@ -59,6 +59,20 @@ module memory_controller ( end /* State Transition Logic */ + /* + * Note: After interrupts were integrated, receiving an interrupt forces IREN + * to go low. On an instruction request, the FSM assumed IREN high, and unconditionally + * proceeded to an instruction wait state (either INSTR_WAIT or INSTR_DATA_REQ). However, + * since IREN was low, the AHB master did not actually receive a request, and therefore the + * I-Bus would never see a "ready" condition; the AHB master would be locked in IDLE, and + * this FSM would be locked in the instruction wait state forever. + * + * To fix, I added logic to abort an instruction request if the IREN signal was low in + * the INSTR_REQ or DATA_INSTR_REQ state; this only happens on an interrupt, so simply aborting + * the transaction on the next transition should be safe since the pipeline will be flushed anyways; + * the instruction request in question should not be fetched since the next instruction should be from + * the interrupt handler after the new PC is inserted. + */ always_comb begin case(current_state) @@ -71,8 +85,10 @@ module memory_controller ( next_state = IDLE; end - INSTR_REQ: begin - if(d_gen_bus_if.ren || d_gen_bus_if.wen) + INSTR_REQ: begin + if(!i_gen_bus_if.ren) // Abort request, received an interrupt + next_state = IDLE; + else if(d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = INSTR_DATA_REQ; else next_state = INSTR_WAIT; @@ -92,8 +108,10 @@ module memory_controller ( next_state = DATA_WAIT; end - DATA_INSTR_REQ: begin - if( out_gen_bus_if.busy == 1'b0 ) + DATA_INSTR_REQ: begin + if(!i_gen_bus_if.ren && out_gen_bus_if.busy == 1'b0) // Abort request, received an interrupt + next_state = IDLE; + else if(out_gen_bus_if.busy == 1'b0) next_state = INSTR_WAIT; else next_state = DATA_INSTR_REQ; diff --git a/sparce.yml b/sparce.yml index d1a4283ff..a012188de 100644 --- a/sparce.yml +++ b/sparce.yml @@ -19,6 +19,9 @@ microarch_params: # Sparisty Optimizations sparce_enabled : "enabled" + # Halt + infinite_loop_halts : "true" + # RISC-MGMT Extension Configuration risc_mgmt_params: standard_extensions: From eb9a2798ec9089fb2a6daad9ea708276ebfce58e Mon Sep 17 00:00:00 2001 From: duanh1028 <60162350+duanh1028@users.noreply.github.com> Date: Wed, 10 Mar 2021 11:43:52 -0500 Subject: [PATCH 07/47] Add files via upload --- .../extensions/rv32m/pp_mul32_uvm/agent.svh | 28 +++++++++ .../rv32m/pp_mul32_uvm/comparator.svh | 61 +++++++++++++++++++ .../rv32m/pp_mul32_uvm/component.svh | 22 +++++++ .../extensions/rv32m/pp_mul32_uvm/driver.svh | 50 +++++++++++++++ .../extensions/rv32m/pp_mul32_uvm/env.svh | 36 +++++++++++ .../extensions/rv32m/pp_mul32_uvm/monitor.svh | 41 +++++++++++++ .../extensions/rv32m/pp_mul32_uvm/mult_if.svh | 13 ++++ .../rv32m/pp_mul32_uvm/predictor.svh | 31 ++++++++++ .../rv32m/pp_mul32_uvm/sequencer.svh | 35 +++++++++++ .../extensions/rv32m/pp_mul32_uvm/tb_mult.sv | 25 ++++++++ .../extensions/rv32m/pp_mul32_uvm/test.svh | 38 ++++++++++++ .../rv32m/pp_mul32_uvm/transaction.svh | 25 ++++++++ 12 files changed, 405 insertions(+) create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/agent.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/comparator.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/component.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/driver.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/env.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/monitor.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/mult_if.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/predictor.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/sequencer.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/tb_mult.sv create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/test.svh create mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/transaction.svh diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/agent.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/agent.svh new file mode 100644 index 000000000..c3b04e208 --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/agent.svh @@ -0,0 +1,28 @@ +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "sequence.svh" +`include "driver.svh" +`include "monitor.svh" + +class agent extends uvm_agent; + `uvm_component_utils(agent) + sequencer sqr; + driver drv; + monitor mon; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + sqr = sequencer::type_id::create("sqr", this); + drv = driver::type_id::create("drv", this); + mon = monitor::type_id::create("mon", this); + endfunction: build_phase + + + virtual function void connect_phase(uvm_phase phase); + drv.seq_item_port.connect(sqr.seq_item_export); + endfunction: connect_phase + +endclass: agent \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/comparator.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/comparator.svh new file mode 100644 index 000000000..e8509bf2b --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/comparator.svh @@ -0,0 +1,61 @@ +import uvm_pkg::*; +`include "uvm_macros.svh" + +class comparator extends uvm_scoreboard; + `uvm_component_utils(comparator) + uvm_analysis_export #(transaction) expected_export; // receive result from predictor + uvm_analysis_export #(transaction) actual_export; // receive result from DUT + uvm_tlm_analysis_fifo #(transaction) expected_fifo; + uvm_tlm_analysis_fifo #(transaction) actual_fifo; + + int m_matches, m_mismatches; // records number of matches and mismatches + + function new( string name , uvm_component parent) ; + super.new( name , parent ); + m_matches = 0; + m_mismatches = 0; + endfunction + + function void build_phase( uvm_phase phase ); + expected_export = new("expected_export", this); + actual_export = new("actual_export", this); + expected_fifo = new("expected_fifo", this); + actual_fifo = new("actual_fifo", this); + endfunction + + function void connect_phase(uvm_phase phase); + expected_export.connect(expected_fifo.analysis_export); + actual_export.connect(actual_fifo.analysis_export); + endfunction + + task run_phase(uvm_phase phase); + transaction expected_tx; //transaction from predictor + transaction actual_tx; //transaction from DUT + forever begin + expected_fifo.get(expected_tx); + actual_fifo.get(actual_tx); + uvm_report_info("Comparator", $psprintf("\nexpected multiplier: %d + \nexpected multiplicand: %d + \nexpected product: %d + \n~~~~~~~~~~~~~~~~~\n + \nactual multiplier: %d + \nactual muliplicand: %d + \nactual product: %d\n", expected_tx.multiplier, expected_tx.multiplicand, expected_tx.product, + actual_tx.multiplier, actual_tx.multiplicand, actual.product)); + + if(expected_tx.compare(actual_tx)) begin + m_matches++; + uvm_report_info("Comparator", "Data Match"); + end else begin + m_mismatches++; + uvm_report_error("Comparator", "Error: Data Mismatch"); + end + end + endtask: connect_phase + + function void report_phase(uvm_phase phase); + uvm_report_info("Comparator", $sformatf("Matches: %0d", m_matches)); + uvm_report_info("Comparator", $sformatf("Mismatches: %0d", m_mismatches)); + endfunction: report_phase + +endclass \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/component.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/component.svh new file mode 100644 index 000000000..2cb245daa --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/component.svh @@ -0,0 +1,22 @@ + +class component extends uvm_component; + `uvm_component_utils(component) + + virtual dut_if pp_mult32_if; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + endfunction: build_phase + + virtual function void connect_phase(uvm_phase phase); + endfunction: connect_phase + + task run_phase(uvm_phase phase); + endtask: run_phase + + function report_phase(uvm_phase phase); + endfunction: report phase +endclass \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/driver.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/driver.svh new file mode 100644 index 000000000..70bbb1d1a --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/driver.svh @@ -0,0 +1,50 @@ +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "mult_if.svh" + +class driver extends uvm_driver#(transaction); + `uvm_component_utils(driver) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction: new + + virtual mult_if vif; + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + if( !uvm_config_db#(virtual mult_if)::get(this, "", "mult_vif", vif) ) begin + `uvm_fatal("Driver", "No interface"); + end + endfunction: build_phase + + task run_phase(uvm_phase phase); + transaction tx; + + forever begin + + @(posedge mult_if.CLK); + seq_item_port.get_next_item(tx); + DUT_reset(); + while(!vif.finished) { + vif.multiplicand = tx.multiplicand; + vif.multiplier = tx.multiplier; + vif.is_signed = 2'b0; + vif.start = 1'b1; + } + + @(posedge vif.clk); + seq_item_port.item_done(); + end + + endtask:run_phase + + + task DUT_reset(); + vif.nRST = 1; + @(posedge vif.CLK); + vif.nRST = 0; + @(posedge vif.CLK); + endtask + +endclass: driver \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/env.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/env.svh new file mode 100644 index 000000000..65511f6ec --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/env.svh @@ -0,0 +1,36 @@ +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "agent.svh" +`include "mult_if.svh" +`include "comparator.svh" // uvm_scoreboard +`include "predictor.svh" // uvm_subscriber +`include "transaction.svh" // uvm_sequence_item + +class environment extends uvm_env; + `uvm_component_utils(environment) + + agent agt; // contains monitor and driver + predictor pred; // a reference model to check the result + comparator comp; // scoreboard + + function new(string name = "env", uvm_component parent = null); + super.new(name, parent); + endfunction + + + + function void build_phase(uvm_phase phase); + // instantiate all the components through factory method + agt = agent::type_id::create("agt", this); + pred = predictor::type_id::create("pred", this); + comp = comparator::type_id::create("comp", this); + + endfunction + + function void connect_phase(uvm_phase phase); + agt.mon.counter_ap.connect(pred.analysis_export); // connect monitor to predictor + pred.pred_ap.connect(comp.expected_export); // connect predictor to comparator + agt.mon.result_ap.connect(comp.actual_export); // connect monitor to comparator + endfunction + +endclass: environment \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/monitor.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/monitor.svh new file mode 100644 index 000000000..deb3592ba --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/monitor.svh @@ -0,0 +1,41 @@ +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "mult_if.svh" + +class monitor extends uvm_monitor; + `uvm_component_utils(monitor) + + virtual mult_if vif; + uvm_analysis_port #(transaction) mult_ap; + uvm_analysis_port #(transaction) result_ap; + transaction prev_tx; + + + virtual function void build_phase(uvm_phase phase); + if (!uvm_config_db#(virtual mult_if)::get(this, "", "mult_vif", vif)) begin + `uvm_fatal("monitor", "No virtual interface specified for this monitor instance") + end + endfunction + + virtual task run_phase(uvm_phase phase); + super.run_phase(phase); + prev_tx = transaction::type_id::create("prev_tx"); + forever begin + transaction tx; + @(posedge vif.clk); + tx = transaction::type_id::create("tx"); + tx.multiplicand = vif.multiplicand; + tx.multiplier = vif.multiplier; + if(!tx.input_equal(prev_tx) && tx.multiplicand != 'z && tx.multiplier != 'z) begin + mult_ap.write(tx); + while(!vif.finished) begin + @(posedge vif.clk) + end + tx.product = vif.product; + result_ap.write(tx); + prev_tx.copy(tx); + end + end + endtask: run_phase + +endclass \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/mult_if.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/mult_if.svh new file mode 100644 index 000000000..efd78f2fd --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/mult_if.svh @@ -0,0 +1,13 @@ + +`ifndef MULT_IF_SVH +`define MULT_IF_SVH + + +interface mult_if(input logic clk) + logic nRST, start, finished; + logic [31:0] multiplicand, multiplier; + logic [1:0] is_signed; + logic [63:0] product; +endinterface: mult_if + +`endif \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/predictor.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/predictor.svh new file mode 100644 index 000000000..cbb63af1e --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/predictor.svh @@ -0,0 +1,31 @@ +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "transaction.svh" + +class predictor extends uvm_subscriber #(transaction); + `uvm_component_utils(predictor) + + uvm_analysis_port #(transaction) pred_ap; + transaction output_tx; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction: new + + function void build_phase(uvm_phase phase); + pred_ap = new("pred_ap", this); + endfunction + + function void write(transaction tx); + output_tx = transaction::type_id::create("output_tx", this); + output_tx.copy(tx); + output_tx.product = 64'b0; + + for(int i = 0; i < tx.multiplier; i++) { + output_tx.product += tx.multiplicand; + } + + pred_ap.write(output_tx); + + endfunction: write +endclass \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/sequencer.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/sequencer.svh new file mode 100644 index 000000000..ca29e21ca --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/sequencer.svh @@ -0,0 +1,35 @@ + +import uvm_pkg::*; +`include "uvm_macros.svh" + +`include "transaction.svh" + +typedef uvm_sequencer #(transaction) sequencer; + + + +class mult_sequence extends uvm_sequence #(transaction); + `uvm_object_utils(counter_sequence) + function new(string name = ""); + super.new(name); + endfunction: new + + task body(); + forever + begin + transaction tx; + tx = transaction::type_id::create("tx"); + start_item(tx); + assert(tx.randomize()); + finish_item(tx); + end + endtask:body +endclass:counter_sequence + + +class sequencer extends uvm_sequence #(transaction); + `uvm_object_utils(mult_sequence) + function new(string name = "sequencer", uvm_component parent=null); + super.new(name); + endfunction: new +endclass: mult_sequencer diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/tb_mult.sv b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/tb_mult.sv new file mode 100644 index 000000000..dfa1f4bb1 --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/tb_mult.sv @@ -0,0 +1,25 @@ +`include "pp_mul32.sv" +`include "mult_if.svh" +`include "test.svh" + +`timescale 1ps/1ps +import uvm_pkg::*; + +module tb_mult(); + logic clk; + + initial begin + clk = 0; + forever #10 clk = !clk; + end + + mult_if pp_mult32_if(clk); + pp_mult32 MULT(pp_mult32_if); + + initial + begin + uvm_config_db#(virtual mult_if)::set( null, "", "vif", fc_if); + run_test("test"); + end + +endmodule: tb_mult; \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/test.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/test.svh new file mode 100644 index 000000000..d1d843faa --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/test.svh @@ -0,0 +1,38 @@ + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "environment.svh" + +class test extends uvm_test; + `uvm_component_utils(test) + + environment env; + virtual mult_if vif; + counter_sequence seq; + + function new(string name = "test", uvm_component parent); + super.new(name, parent); + endfunction: new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + env = environment::type_id::create("env",this); + seq = mult_sequence::type_id::create("seq"); + + if (!uvm_config_db#(virtual mult_if)::get(this, "", "vif", vif)) begin + `uvm_fatal("TEST", "No virtual interface specified for this test instance") + end + uvm_config_db#(virtual counter_if)::set(this, "env.agt*", "vif", vif); + + + endfunction: build_phase + + task run_phase(uvm_phase phase); + phase.raise_objection( this, "Starting sequence in main phase" ); + $display("%t Starting sequence run_phase",$time); + seq.start(env.agt.sqr); + #100ns; + phase.drop_objection( this , "Finished in main phase" ); + endtask + +endclass: test \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/transaction.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/transaction.svh new file mode 100644 index 000000000..0fc0c70be --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/transaction.svh @@ -0,0 +1,25 @@ +`ifndef TRANSACTION_SVH +`define TRANSACTION_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" + +class transaction extends uvm_sequence_item; + `uvm_object_utils(transaction) + rand bit [31:0] multiplicand; + rand bit [31:0] multiplier; + rand bit [1:0] is_signed; + bit finished; + bit [63:0] product; + + constraint c_multiplicand {multiplicand > 0; multiplicand < 31'b100000}; + constraint c_multiplier {multiplier > 0; multiplier < 31'b100000}; + + + function new(string name = "transaction"); + super.new(name); + endfunction: new + +endclass: transaction + +`endif \ No newline at end of file From 712b3000239de7db5fbf2f59c1016a3ea512dae0 Mon Sep 17 00:00:00 2001 From: duanh1028 <60162350+duanh1028@users.noreply.github.com> Date: Wed, 10 Mar 2021 13:37:25 -0500 Subject: [PATCH 08/47] Add files via upload --- .../risc_mgmt/extensions/rv32m/Makefile | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 source_code/risc_mgmt/extensions/rv32m/Makefile diff --git a/source_code/risc_mgmt/extensions/rv32m/Makefile b/source_code/risc_mgmt/extensions/rv32m/Makefile new file mode 100644 index 000000000..d312ae99c --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/Makefile @@ -0,0 +1,49 @@ +TOP = tb_mult.sv +AGENT = tb/agent +TEST = tb/test +ENV = tb/env +SEQ = tb/sequencer +COMP = tb/comparator +DRIVER = tb/driver +MONITOR = tb/monitor +MULT_if = tb/mult_if +PRED = tb/predictor +TRANS = tb/transaction +REG_MODEL = $(REG)/vreguvm_pkg_uvm.sv + + +all: build run + +build: + vlog +incdir+src \ + +incdir+$(TOP) \ + +incdir+$(AGENT) \ + +incdir+$(TEST) \ + +incdir+$(ENV) \ + +incdir+$(SEQ) \ + +incdir+$(COMP) \ + +incdir+$(DRIVER) \ + +incdir+$(MONITOR) \ + +incdir+$(MULT_if) \ + +incdir+$(PRED) \ + +incdir+$(TRANS) \ + +acc \ + +cover \ + -L $$QUESTA_HOME/uvm-1.2 $(REG_MODEL) tb/$(TOP) + +run: + vsim -i tb_mult -L \ + $$QUESTA_HOME/uvm-1.2 \ + -voptargs=+acc \ + -coverage \ + +UVM_TESTNAME="mult_test" \ + +UVM_VERBOSITY=UVM_LOW \ + -do "do wave.do" -do "run -all" + +clean: + rm -rf work + rm -rf mitll90_Dec2019_all + rm -rf covhtmlreport + rm *.log + rm transcript + rm *.wlf From 03b81da5fba3e672212c850317b64b093db0305d Mon Sep 17 00:00:00 2001 From: jingyin77 <54422727+jingyin77@users.noreply.github.com> Date: Mon, 15 Mar 2021 19:24:26 -0400 Subject: [PATCH 09/47] Compressed (#6) Initial implementation of RV32C. Contains instruction decompression logic and fetch buffer to allow for free mixing of 16- and 32-bit instructions. Also includes self-tests for all currently-implemented RV32C instructions and updates to config_core.py to allow RV32C to be enabled/disabled. Author: Jing Yin See --- example.yml | 5 +- scripts/config_core.py | 2 + source_code/include/decompressor_if.vh | 21 + source_code/include/fetch_buffer_if.vh | 18 + source_code/include/risc_mgmt_if.vh | 7 +- source_code/include/rv32c_if.vh | 30 ++ source_code/pipelines/pipeline_wrapper.sv | 1 + source_code/pipelines/tspp/tspp.sv | 1 + .../pipelines/tspp/tspp_execute_stage.sv | 18 +- .../pipelines/tspp/tspp_fetch_stage.sv | 40 +- source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv | 1 + source_code/rv32c/decompressor.sv | 201 ++++++++++ source_code/rv32c/fetch_buffer.sv | 144 +++++++ source_code/rv32c/rv32c_disabled.sv | 22 ++ source_code/rv32c/rv32c_enabled.sv | 42 ++ source_code/rv32c/rv32c_wrapper.sv | 16 + source_code/standard_core/RISCVBusiness.sv | 15 +- source_code/standard_core/control_unit.sv | 1 + source_code/wscript | 2 +- sparce.yml | 3 + verification/asm-env/selfasm/test_macros.h | 361 ++++++++++++++++++ verification/self-tests/RV32C/c_add.S | 81 ++++ verification/self-tests/RV32C/c_addi.S | 63 +++ verification/self-tests/RV32C/c_addi16sp.S | 65 ++++ verification/self-tests/RV32C/c_addi4spn.S | 66 ++++ verification/self-tests/RV32C/c_and.S | 64 ++++ verification/self-tests/RV32C/c_andi.S | 47 +++ verification/self-tests/RV32C/c_beqz.S | 60 +++ verification/self-tests/RV32C/c_bnez.S | 59 +++ verification/self-tests/RV32C/c_j.S | 86 +++++ verification/self-tests/RV32C/c_jal.S | 86 +++++ verification/self-tests/RV32C/c_jalr.S | 89 +++++ verification/self-tests/RV32C/c_jr.S | 89 +++++ verification/self-tests/RV32C/c_li.S | 34 ++ verification/self-tests/RV32C/c_lui.S | 34 ++ verification/self-tests/RV32C/c_lw.S | 77 ++++ verification/self-tests/RV32C/c_lwsp.S | 77 ++++ verification/self-tests/RV32C/c_mv.S | 85 +++++ verification/self-tests/RV32C/c_or.S | 62 +++ verification/self-tests/RV32C/c_slli.S | 60 +++ verification/self-tests/RV32C/c_srai.S | 60 +++ verification/self-tests/RV32C/c_srli.S | 59 +++ verification/self-tests/RV32C/c_sub.S | 77 ++++ verification/self-tests/RV32C/c_sw.S | 75 ++++ verification/self-tests/RV32C/c_swsp.S | 75 ++++ verification/self-tests/RV32C/c_xor.S | 62 +++ 46 files changed, 2617 insertions(+), 26 deletions(-) create mode 100644 source_code/include/decompressor_if.vh create mode 100644 source_code/include/fetch_buffer_if.vh create mode 100644 source_code/include/rv32c_if.vh create mode 100644 source_code/rv32c/decompressor.sv create mode 100644 source_code/rv32c/fetch_buffer.sv create mode 100644 source_code/rv32c/rv32c_disabled.sv create mode 100644 source_code/rv32c/rv32c_enabled.sv create mode 100644 source_code/rv32c/rv32c_wrapper.sv create mode 100644 verification/self-tests/RV32C/c_add.S create mode 100644 verification/self-tests/RV32C/c_addi.S create mode 100644 verification/self-tests/RV32C/c_addi16sp.S create mode 100644 verification/self-tests/RV32C/c_addi4spn.S create mode 100644 verification/self-tests/RV32C/c_and.S create mode 100644 verification/self-tests/RV32C/c_andi.S create mode 100644 verification/self-tests/RV32C/c_beqz.S create mode 100644 verification/self-tests/RV32C/c_bnez.S create mode 100644 verification/self-tests/RV32C/c_j.S create mode 100644 verification/self-tests/RV32C/c_jal.S create mode 100644 verification/self-tests/RV32C/c_jalr.S create mode 100644 verification/self-tests/RV32C/c_jr.S create mode 100644 verification/self-tests/RV32C/c_li.S create mode 100644 verification/self-tests/RV32C/c_lui.S create mode 100644 verification/self-tests/RV32C/c_lw.S create mode 100644 verification/self-tests/RV32C/c_lwsp.S create mode 100644 verification/self-tests/RV32C/c_mv.S create mode 100644 verification/self-tests/RV32C/c_or.S create mode 100644 verification/self-tests/RV32C/c_slli.S create mode 100644 verification/self-tests/RV32C/c_srai.S create mode 100644 verification/self-tests/RV32C/c_srli.S create mode 100644 verification/self-tests/RV32C/c_sub.S create mode 100644 verification/self-tests/RV32C/c_sw.S create mode 100644 verification/self-tests/RV32C/c_swsp.S create mode 100644 verification/self-tests/RV32C/c_xor.S diff --git a/example.yml b/example.yml index 85c79f5d2..8dca312d4 100644 --- a/example.yml +++ b/example.yml @@ -19,8 +19,11 @@ microarch_params: # Sparisty Optimizations sparce_enabled : "disabled" + # RV32C + rv32c_enabled : "disabled" + # Halt - infinite_loop_halts: "true" + infinite_loop_halts : "true" # RISC-MGMT Extension Configuration risc_mgmt_params: diff --git a/scripts/config_core.py b/scripts/config_core.py index b75d3c24a..132ba1944 100755 --- a/scripts/config_core.py +++ b/scripts/config_core.py @@ -47,6 +47,8 @@ 'bus_interface_type' : ['ahb_if', 'generic_bus_if'], # Sparisty Optimizations 'sparce_enabled' : [ 'enabled', 'disabled' ], + # RV32C + 'rv32c_enabled' : [ 'enabled', 'disabled' ] # Halt Enable -- Good for testing, not for tapeout 'infinite_loop_halts' : ['true', 'false'] } diff --git a/source_code/include/decompressor_if.vh b/source_code/include/decompressor_if.vh new file mode 100644 index 000000000..0646f161e --- /dev/null +++ b/source_code/include/decompressor_if.vh @@ -0,0 +1,21 @@ +`ifndef DECOMPRESSOR_IF_VH +`define DECOMPRESSOR_IF_VH + +interface decompressor_if(); + import rv32i_types_pkg::word_t; + word_t inst32; + logic [15:0] inst16; + logic edit_rs1, edit_rs2, edit_rd, c_ena; + + modport dcpr ( + input inst16, + output inst32, c_ena, edit_rs1, edit_rs2, edit_rd + ); + + modport cu ( + input edit_rs1, edit_rs2, edit_rd, c_ena + ); + +endinterface + +`endif //DECOMPRESSOR_IF_VH diff --git a/source_code/include/fetch_buffer_if.vh b/source_code/include/fetch_buffer_if.vh new file mode 100644 index 000000000..84c6f2514 --- /dev/null +++ b/source_code/include/fetch_buffer_if.vh @@ -0,0 +1,18 @@ +`ifndef FETCH_BUFFER_IF_VH +`define FETCH_BUFFER_IF_VH + +interface fetch_buffer_if(); + import rv32i_types_pkg::word_t; + + + word_t inst, reset_pc, nextpc, imem_pc, result, reset_pc_val; + logic reset_en, inst_arrived, pc_update, done, done_earlier, done_earlier_send, ex_busy; + + modport fb ( + input inst, reset_en, reset_pc, inst_arrived, pc_update, ex_busy, reset_pc_val, + output done, nextpc, imem_pc, result, done_earlier, done_earlier_send + ); + +endinterface + +`endif //FETCH_BUFFER_IF_VH diff --git a/source_code/include/risc_mgmt_if.vh b/source_code/include/risc_mgmt_if.vh index 1722afeb2..c8ef203d8 100644 --- a/source_code/include/risc_mgmt_if.vh +++ b/source_code/include/risc_mgmt_if.vh @@ -69,12 +69,15 @@ interface risc_mgmt_if (); logic exception; logic [`NUM_EXTENSIONS-1:0] ex_cause; + // to stall rv32c + logic [`NUM_EXTENSIONS-1:0] risc_mgmt_start; + modport ts_rmgmt ( output req_reg_r, req_reg_w, rsel_s_0, rsel_s_1, rsel_d, reg_w, reg_wdata, req_br_j, branch_jump, br_j_addr, req_mem, mem_addr, mem_store, mem_ren, mem_wen, mem_byte_en, execute_stall, memory_stall, - active_insn, exception, ex_cause, ex_token, + active_insn, exception, ex_cause, ex_token, risc_mgmt_start, input rdata_s_0, rdata_s_1, mem_load, mem_busy, insn, pc, if_ex_enable ); @@ -92,7 +95,7 @@ interface risc_mgmt_if (); input req_reg_r, req_reg_w, rsel_s_0, rsel_s_1, rsel_d, reg_w, reg_wdata, req_br_j, branch_jump, br_j_addr, req_mem, mem_addr, mem_store, mem_ren, - mem_wen, mem_byte_en, ex_token + mem_wen, mem_byte_en, ex_token, risc_mgmt_start ); modport ts_hazard ( diff --git a/source_code/include/rv32c_if.vh b/source_code/include/rv32c_if.vh new file mode 100644 index 000000000..c2a3d1d5b --- /dev/null +++ b/source_code/include/rv32c_if.vh @@ -0,0 +1,30 @@ +`ifndef RV32C_IF_VH +`define RV32C_IF_VH + +interface rv32c_if(); + + import rv32i_types_pkg::word_t; + + word_t inst, reset_pc, nextpc, imem_pc, result, inst32, reset_pc_val; + logic [15:0] inst16; + logic reset_en, inst_arrived, pc_update, done, c_ena, rv32c_ena, done_earlier, done_earlier_send, halt, ex_busy; + + + modport rv32c ( + input inst, reset_en, reset_pc, inst_arrived, pc_update, inst16, halt, ex_busy, reset_pc_val, + output done, nextpc, imem_pc, result, inst32, c_ena, rv32c_ena, done_earlier, done_earlier_send + ); + + modport fetch ( + input done, nextpc, imem_pc, result, rv32c_ena, done_earlier, done_earlier_send, + output inst, reset_en, reset_pc, inst_arrived, pc_update, reset_pc_val + ); + + modport execute ( + input inst32, c_ena, done_earlier, + output inst16, halt, ex_busy + ); + +endinterface + +`endif //RV32C_IF_VH diff --git a/source_code/pipelines/pipeline_wrapper.sv b/source_code/pipelines/pipeline_wrapper.sv index a9c9e22dd..9689f182c 100644 --- a/source_code/pipelines/pipeline_wrapper.sv +++ b/source_code/pipelines/pipeline_wrapper.sv @@ -29,6 +29,7 @@ `include "cache_control_if.vh" `include "component_selection_defines.vh" `include "sparce_pipeline_if.vh" +`include "rv32c_if.vh" module pipeline_wrapper ( input logic CLK, nRST, diff --git a/source_code/pipelines/tspp/tspp.sv b/source_code/pipelines/tspp/tspp.sv index f6ba969c7..f436c7787 100644 --- a/source_code/pipelines/tspp/tspp.sv +++ b/source_code/pipelines/tspp/tspp.sv @@ -30,6 +30,7 @@ `include "risc_mgmt_if.vh" `include "cache_control_if.vh" `include "sparce_pipeline_if.vh" +`include "rv32c_if.vh" module tspp ( input logic CLK, nRST, diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index 540cc855c..63ee5700a 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -33,7 +33,7 @@ `include "prv_pipeline_if.vh" `include "risc_mgmt_if.vh" `include "cache_control_if.vh" - +`include "rv32c_if.vh" module tspp_execute_stage( input logic CLK, nRST, @@ -45,7 +45,8 @@ module tspp_execute_stage( output logic halt, risc_mgmt_if.ts_execute rm_if, cache_control_if.pipeline cc_if, - sparce_pipeline_if.pipe_execute sparce_if + sparce_pipeline_if.pipe_execute sparce_if, + rv32c_if.execute rv32cif ); import rv32i_types_pkg::*; @@ -99,7 +100,7 @@ module tspp_execute_stage( assign rm_if.rdata_s_1 = rf_if.rs2_data; - /******************************************************* + /******************************************************** *** Choose the Endianness Coming into the processor *******************************************************/ generate @@ -114,8 +115,13 @@ module tspp_execute_stage( end endgenerate - assign cu_if.instr = fetch_ex_if.fetch_ex_reg.instr; - assign rm_if.insn = fetch_ex_if.fetch_ex_reg.instr; + //RV32C + assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; + assign rv32cif.halt = cu_if.halt; + assign rv32cif.ex_busy = cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; + assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + /******************************************************* *** Sign Extensions @@ -182,7 +188,7 @@ module tspp_execute_stage( end end - assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) & (~hazard_if.if_ex_stall | hazard_if.npc_sel) & + assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) & (~hazard_if.if_ex_stall | hazard_if.npc_sel | rv32cif.done_earlier) & ~(cu_if.dren & mal_addr); /******************************************************* *** Branch Target Resolution and Associated Logic diff --git a/source_code/pipelines/tspp/tspp_fetch_stage.sv b/source_code/pipelines/tspp/tspp_fetch_stage.sv index 37a30dbf6..5a1cbdcc9 100644 --- a/source_code/pipelines/tspp/tspp_fetch_stage.sv +++ b/source_code/pipelines/tspp/tspp_fetch_stage.sv @@ -28,6 +28,7 @@ `include "generic_bus_if.vh" `include "component_selection_defines.vh" `include "cache_control_if.vh" +`include "rv32c_if.vh" module tspp_fetch_stage ( input logic CLK, nRST, @@ -35,53 +36,64 @@ module tspp_fetch_stage ( tspp_hazard_unit_if.fetch hazard_if, predictor_pipeline_if.access predict_if, generic_bus_if.cpu igen_bus_if, - sparce_pipeline_if.pipe_fetch sparce_if + sparce_pipeline_if.pipe_fetch sparce_if, + rv32c_if.fetch rv32cif ); import rv32i_types_pkg::*; - parameter RESET_PC = 32'h200; - //parameter RESET_PC = 32'h80000000; + ///parameter RESET_PC = 32'h200; + parameter RESET_PC = 32'h80000000; - word_t pc, pc4, npc, instr; + word_t pc, pc4or2, npc, instr; //PC logic always_ff @ (posedge CLK, negedge nRST) begin if(~nRST) begin pc <= RESET_PC; - end else if (hazard_if.pc_en) begin + end else if (hazard_if.pc_en | rv32cif.done_earlier) begin pc <= npc; end end - assign pc4 = pc + 4; + //RV32C + assign rv32cif.inst = igen_bus_if.rdata; + assign rv32cif.inst_arrived = hazard_if.if_ex_flush == 0 & hazard_if.if_ex_stall == 0; + assign rv32cif.reset_en = hazard_if.insert_priv_pc | sparce_if.skipping | hazard_if.npc_sel | predict_if.predict_taken; + assign rv32cif.pc_update = hazard_if.pc_en; + assign rv32cif.reset_pc = npc; + assign rv32cif.reset_pc_val = RESET_PC; + + assign pc4or2 = (rv32cif.rv32c_ena & (rv32cif.result[1:0] != 2'b11)) ? (pc + 2) : (pc + 4); assign predict_if.current_pc = pc; assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc : ( sparce_if.skipping ? sparce_if.sparce_target : (hazard_if.npc_sel ? fetch_ex_if.brj_addr : - (predict_if.predict_taken ? predict_if.target_addr : pc4))); + (predict_if.predict_taken ? predict_if.target_addr : rv32cif.rv32c_ena ? rv32cif.nextpc : pc4or2))); //Instruction Access logic assign hazard_if.i_mem_busy = igen_bus_if.busy; - assign igen_bus_if.addr = pc; - assign igen_bus_if.ren = hazard_if.iren; + assign igen_bus_if.addr = rv32cif.rv32c_ena ? rv32cif.imem_pc : pc; + assign igen_bus_if.ren = hazard_if.iren & !rv32cif.done_earlier; assign igen_bus_if.wen = 1'b0; assign igen_bus_if.byte_en = 4'b1111; assign igen_bus_if.wdata = '0; //Fetch Execute Pipeline Signals + word_t instr_to_ex; + assign instr_to_ex = rv32cif.rv32c_ena ? rv32cif.result : igen_bus_if.rdata; always_ff @ (posedge CLK, negedge nRST) begin if (!nRST) fetch_ex_if.fetch_ex_reg <= '0; else if (hazard_if.if_ex_flush) fetch_ex_if.fetch_ex_reg <= '0; - else if (!hazard_if.if_ex_stall) begin + else if (((rv32cif.done | rv32cif.done_earlier) & rv32cif.rv32c_ena) | (!hazard_if.if_ex_stall & !rv32cif.rv32c_ena)) begin fetch_ex_if.fetch_ex_reg.token <= 1'b1; fetch_ex_if.fetch_ex_reg.pc <= pc; - fetch_ex_if.fetch_ex_reg.pc4 <= pc4; - fetch_ex_if.fetch_ex_reg.instr <= igen_bus_if.rdata; + fetch_ex_if.fetch_ex_reg.pc4 <= pc4or2; + fetch_ex_if.fetch_ex_reg.instr <= instr_to_ex; fetch_ex_if.fetch_ex_reg.prediction <= predict_if.predict_taken; - end + end end - + //Send exceptions to Hazard Unit logic mal_addr; assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); diff --git a/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv b/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv index ce5414617..b859357ae 100644 --- a/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv +++ b/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv @@ -139,6 +139,7 @@ module tspp_risc_mgmt ( end assign e_start = {N_EXTENSIONS{ex_start}} & tokens; + assign rm_if.risc_mgmt_start = e_start; // to stall rv32c load buffer /* Registerfile / Forwarding Logic * Forwarding not present in 2 stage pipeline diff --git a/source_code/rv32c/decompressor.sv b/source_code/rv32c/decompressor.sv new file mode 100644 index 000000000..95565d0ed --- /dev/null +++ b/source_code/rv32c/decompressor.sv @@ -0,0 +1,201 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: decompressor.sv +* +* Created by: Jing Yin See +* Email: see4@purdue.edu +* Date Created: 12/20/2020 +* Description: Decompress RV32C instructions into full-size instruction +*/ + +`include "decompressor_if.vh" +module decompressor +( + decompressor_if.dcpr dcpr_if +); + logic c0_format, c1_format, c2_format; + logic upper3_0, upper3_1, upper3_2, upper3_3, upper3_4, upper3_5, upper3_6, upper3_7; + logic c_addi, c_addi16sp, c_addi14spn, c_slli, c_andi, c_srli, c_srai; + logic c_mv, c_add, c_and, c_or, c_xor, c_sub; + logic c_lw, c_sw, c_lwsp, c_swsp, c_flw, c_fsw, c_flwsp, c_fswsp, c_fld, c_fsd, c_fldsp, c_fsdsp; + logic c_j, c_jal, c_jr, c_jalr, c_beqz, c_bnez; + logic c_li, c_lui, c_nop, c_ebreak; + logic rtype, itype, stype, btype, utype, jtype; + logic [2:0] funct3; + logic [6:0] funct7; + logic [5:0] imm_i_c; + logic [11:0] imm_i; + logic [10:0] imm_j; + logic [7:0] imm_b; + logic [4:0] rd, rs2; + logic [4:0] offset_c, offset_c_df; + logic [5:0] offset_csp, offset_csp_df; + logic [11:0] offset, offset_df; + logic [20:0] jump_offset; + logic [12:0] branch_offset; + + assign c0_format = dcpr_if.inst16[1:0] == 2'b00; + assign c1_format = dcpr_if.inst16[1:0] == 2'b01; + assign c2_format = dcpr_if.inst16[1:0] == 2'b10; + + assign upper3_0 = dcpr_if.inst16[15:13] == 3'b000; + assign upper3_1 = dcpr_if.inst16[15:13] == 3'b001; + assign upper3_2 = dcpr_if.inst16[15:13] == 3'b010; + assign upper3_3 = dcpr_if.inst16[15:13] == 3'b011; + assign upper3_4 = dcpr_if.inst16[15:13] == 3'b100; + assign upper3_5 = dcpr_if.inst16[15:13] == 3'b101; + assign upper3_6 = dcpr_if.inst16[15:13] == 3'b110; + assign upper3_7 = dcpr_if.inst16[15:13] == 3'b111; + + // Immediate + assign c_addi = upper3_0 & (dcpr_if.inst16[11:7] != 5'd0) & c1_format; + assign c_addi16sp = upper3_3 & (dcpr_if.inst16[11:7] == 5'd2) & c1_format; + assign c_addi14spn = upper3_0 & (dcpr_if.inst16[12:5] != 8'd0) & c0_format; + assign c_slli = upper3_0 & c2_format; + assign c_andi = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd2); + assign c_srli = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd0); + assign c_srai = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd1); + + // Register + assign c_mv = upper3_4 & c2_format & dcpr_if.inst16[12] == 1'b0 & dcpr_if.inst16[6:2] != 5'd0; + assign c_add = upper3_4 & c2_format & dcpr_if.inst16[12] & dcpr_if.inst16[6:2] != 5'd0 & dcpr_if.inst16[11:7] != 5'd0; + assign c_and = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) & (dcpr_if.inst16[6:5] == 2'd3); + assign c_or = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) & (dcpr_if.inst16[6:5] == 2'd2); + assign c_xor = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) & (dcpr_if.inst16[6:5] == 2'd1); + assign c_sub = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) & (dcpr_if.inst16[6:5] == 2'd0); + + // Load/Store + assign c_lw = upper3_2 & c0_format; + assign c_sw = upper3_6 & c0_format; + assign c_flw = upper3_3 & c0_format; + assign c_fsw = upper3_7 & c0_format; + assign c_fld = upper3_1 & c0_format; + assign c_fsd = upper3_5 & c0_format; + assign c_lwsp = upper3_2 & c2_format; + assign c_swsp = upper3_6 & c2_format; + assign c_flwsp = upper3_3 & c2_format; + assign c_fswsp = upper3_7 & c2_format; + assign c_fldsp = upper3_1 & c2_format; + assign c_fsdsp = upper3_5 & c2_format; + + // Control Transfer + assign c_j = upper3_5 & c1_format; + assign c_jal = upper3_1 & c1_format; + assign c_jr = upper3_4 & dcpr_if.inst16[12] == 1'b0 & dcpr_if.inst16[6:2] == 5'd0 & c2_format; + assign c_jalr = upper3_4 & c2_format & dcpr_if.inst16[12] & dcpr_if.inst16[6:2] == 5'd0 & dcpr_if.inst16[11:7] != 5'd0; + assign c_beqz = upper3_6 & c1_format; + assign c_bnez = upper3_7 & c1_format; + + // Constant Generation + assign c_li = upper3_2 & c1_format; + assign c_lui = upper3_3 & (dcpr_if.inst16[11:7] != 5'd0) & (dcpr_if.inst16[11:7] != 5'd2) & c1_format; + + // Others + //assign illegal = dcpr_if.inst16 == 16'd0; + assign c_nop = upper3_0 & (dcpr_if.inst16[12:2] == 11'd0) & c1_format; + assign c_ebreak = upper3_4 & c2_format & dcpr_if.inst16[12] & dcpr_if.inst16[11:2] == 10'd0; + + // Inst type + assign rtype = c_mv | c_add | c_and | c_or | c_xor | c_sub; + assign itype = c_addi | c_addi16sp | c_addi14spn | c_slli | c_andi | c_srli | c_lw | c_flw | c_lwsp | c_flwsp | c_li | c_nop | c_srai | c_fld | c_fldsp; + assign stype = c_sw | c_swsp | c_fsw | c_fswsp | c_fsd | c_fsdsp; + assign btype = c_beqz | c_bnez; + assign utype = c_lui; + assign jtype = c_j | c_jal | c_jr | c_jalr; + + assign imm_i_c = {dcpr_if.inst16[12], dcpr_if.inst16[6:2]}; + assign imm_j = {dcpr_if.inst16[12], dcpr_if.inst16[8], dcpr_if.inst16[10:9], dcpr_if.inst16[6], dcpr_if.inst16[7], dcpr_if.inst16[2], dcpr_if.inst16[11], dcpr_if.inst16[5:3]}; + assign imm_b = {dcpr_if.inst16[12], dcpr_if.inst16[6:5], dcpr_if.inst16[2], dcpr_if.inst16[11:10], dcpr_if.inst16[4:3]}; + assign offset_c = {dcpr_if.inst16[5], dcpr_if.inst16[12:10], dcpr_if.inst16[6]}; + assign offset_csp = (c_swsp | c_fswsp) ? {dcpr_if.inst16[8:7], dcpr_if.inst16[12:9]} : {dcpr_if.inst16[3:2], dcpr_if.inst16[12], dcpr_if.inst16[6:4]}; + assign offset_c_df = {dcpr_if.inst16[6:5], dcpr_if.inst16[12:10]}; + assign offset_csp_df = c_fsdsp ? {dcpr_if.inst16[9:7], dcpr_if.inst16[12:10]} : {dcpr_if.inst16[4:2], dcpr_if.inst16[12], dcpr_if.inst16[6:5]}; + + // Sign Extend immediate + always_comb begin + if (c_srai) imm_i = {7'b0100000, imm_i_c[4:0]}; + else if (c_srli | c_slli) imm_i = {7'b0000000, imm_i_c[4:0]}; + else if (c_addi16sp) imm_i = {{2{dcpr_if.inst16[12]}}, {dcpr_if.inst16[12], dcpr_if.inst16[4:3], dcpr_if.inst16[5], dcpr_if.inst16[2], dcpr_if.inst16[6]}, 4'd0}; + else if (c_addi14spn) imm_i = {2'd0, {dcpr_if.inst16[10:7], dcpr_if.inst16[12], dcpr_if.inst16[11], dcpr_if.inst16[5], dcpr_if.inst16[6]}, 2'd0}; + else imm_i = {{6{imm_i_c[5]}}, imm_i_c}; + end + + // Sign Extend offset + assign offset = (c_sw | c_lw | c_fsw | c_flw) ? {5'd0, offset_c, 2'd0} : {4'd0, offset_csp, 2'd0}; + assign offset_df = (c_fld | c_fsd) ? {4'd0, offset_c_df, 3'd0} : {3'd0, offset_csp_df, 3'd0}; + assign jump_offset = {{9{imm_j[10]}}, imm_j, 1'b0}; + assign branch_offset = {{4{imm_b[7]}}, imm_b, 1'b0}; + + // Select funct3 in 32 bit instruction + always_comb begin +// if (c_addi | c_addi16sp | c_addi14spn | c_add | c_sub | c_mv | c_li | c_nop | c_beqz) funct3 = 3'b000; + if (c_slli | c_bnez) funct3 = 3'b001; + else if (c_andi | c_and) funct3 = 3'b111; + else if (c_srli | c_srai) funct3 = 3'b101; + else if (c_or) funct3 = 3'b110; + else if (c_xor) funct3 = 3'b100; + else if (c_sw | c_fsw | c_swsp | c_fswsp | c_lw | c_flw | c_lwsp | c_flwsp) funct3 = 3'b010; + else if (c_fld| c_fsd | c_fldsp | c_fsdsp) funct3 = 3'b011; + else funct3 = 3'b000; + end + + // Select register + assign rd = (c_srli | c_srai | c_and | c_or | c_xor | c_sub | c_sw | c_lw | c_fsw | c_fsd | c_flw | c_fld | c_beqz | c_bnez | c_andi) ? {2'd1, dcpr_if.inst16[9:7]} : dcpr_if.inst16[11:7]; + assign rs2 = (c_mv | c_add) ? dcpr_if.inst16[6:2] : {2'd1, dcpr_if.inst16[4:2]}; + + // Encode full 32 bit instruction + always_comb begin + if (rtype) begin + if (c_sub) dcpr_if.inst32 = {7'b0100000, rs2, rd, funct3, rd, 7'b0110011}; + else if (c_mv) dcpr_if.inst32 = {7'b0000000, rs2, 5'd0, funct3, rd, 7'b0110011}; + else dcpr_if.inst32 = {7'b0000000, rs2, rd, funct3, rd, 7'b0110011}; + end else if (itype) begin + if (c_addi16sp) dcpr_if.inst32 = {imm_i, 5'd2, funct3, 5'd2, 7'b0010011}; + else if (c_addi14spn) dcpr_if.inst32 = {imm_i, 5'd2, funct3, rs2, 7'b0010011}; + else if (c_li) dcpr_if.inst32 = {imm_i, 5'd0, funct3, rd, 7'b0010011}; + else if (c_lw) dcpr_if.inst32 = {offset, rd, funct3, rs2, 7'b0000011}; + else if (c_flw) dcpr_if.inst32 = {offset, rd, funct3, rs2, 7'b0000111}; + else if (c_fld) dcpr_if.inst32 = {offset_df, rd, funct3, rs2, 7'b0000111}; + else if (c_lwsp) dcpr_if.inst32 = {offset, 5'd2, funct3, rd, 7'b0000011}; + else if (c_flwsp) dcpr_if.inst32 = {offset, 5'd2, funct3, rd, 7'b0000111}; + else if (c_fldsp) dcpr_if.inst32 = {offset_df, 5'd2, funct3, rd, 7'b0000111}; + else if (c_nop) dcpr_if.inst32 = {17'd0, funct3, 5'd0, 7'b0010011}; + else dcpr_if.inst32 = {imm_i, rd, funct3, rd, 7'b0010011}; + end else if (stype) begin + if (c_sw) dcpr_if.inst32 = {offset[11:5], rs2, rd, funct3, offset[4:0], 7'b0100011}; + else if (c_fsw) dcpr_if.inst32 = {offset[11:5], rs2, rd, funct3, offset[4:0], 7'b0100111}; + else if (c_fsd) dcpr_if.inst32 = {offset_df[11:5], rs2, rd, funct3, offset_df[4:0], 7'b0100111}; + else if (c_fswsp) dcpr_if.inst32 = {offset[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset[4:0], 7'b0100111}; + else if (c_fsdsp) dcpr_if.inst32 = {offset_df[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset_df[4:0], 7'b0100111}; + else dcpr_if.inst32 = {offset[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset[4:0], 7'b0100011}; + end else if (btype) dcpr_if.inst32 = {branch_offset[12], branch_offset[10:5], 5'd0, rd, funct3, branch_offset[4:1], branch_offset[11], 7'b1100011}; + else if (utype) dcpr_if.inst32 = {{{14{imm_i_c[5]}}, imm_i_c}, rd, 7'b0110111}; + else if (jtype) begin + if (c_j) dcpr_if.inst32 = {jump_offset[20], jump_offset[10:1], jump_offset[11], jump_offset[19:12], 5'd0, 7'b1101111}; + else if (c_jal) dcpr_if.inst32 = {jump_offset[20], jump_offset[10:1], jump_offset[11], jump_offset[19:12], 5'd1, 7'b1101111}; + else if (c_jr) dcpr_if.inst32 = {12'd0, rd, 3'd0, 5'd0, 7'b1100111}; + else dcpr_if.inst32 = {12'd0, rd, 3'd0, 5'd1, 7'b1100111}; + end else if (c_ebreak) dcpr_if.inst32 = {12'b000000000001, 5'd0, 3'd0, 5'd0, 7'b1110011}; + else dcpr_if.inst32 = 32'd0; + end + +// assign dcpr_if.edit_rd = c_addi14spn | c_lw | c_and | c_sub | c_or | c_xor | c_srli | c_srai | c_andi; +// assign dcpr_if.edit_rs1 = c_lw | c_sw | c_and | c_sub | c_or | c_xor | c_beqz | c_bnez | c_srli | c_srai | c_andi; +// assign dcpr_if.edit_rs2 = c_sw | c_and | c_sub | c_or | c_xor; + assign dcpr_if.c_ena = dcpr_if.inst16[1:0] != 2'b11; +endmodule + diff --git a/source_code/rv32c/fetch_buffer.sv b/source_code/rv32c/fetch_buffer.sv new file mode 100644 index 000000000..c70c853b8 --- /dev/null +++ b/source_code/rv32c/fetch_buffer.sv @@ -0,0 +1,144 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: fetch_buffer.sv +* +* Created by: Jing Yin See +* Email: see4@purdue.edu +* Date Created: 01/15/2021 +* Description: Generate next pc and instruction memory pc, output instructions +*/ + +`include "fetch_buffer_if.vh" +module fetch_buffer +( + input logic clk, n_rst, + fetch_buffer.fb fb_if +); + logic [15:0] buffer, nextbuffer; + logic [31:0] pc, next_imem_pc; + logic combine, combine_reg, waitnext, waitnext_reg; + logic [31:0] final_inst_store, final_inst; + logic reset_next, finished; + logic inst_arrived_delay; + + always_ff @ (posedge clk, negedge n_rst) begin + if (n_rst == 0) inst_arrived_delay <= 1'b0; + else inst_arrived_delay <= fb_if.inst_arrived; + end + + + assign fb_if.done_earlier = inst_arrived_delay & waitnext_reg & !fb_if.ex_busy; + //assign fb_if.done_earlier = 0; + + // Buffer and PC logic + always_ff @ (posedge clk, negedge n_rst) begin + if (n_rst == 0) begin + buffer <= 16'd0; + combine_reg <= 1'b0; + waitnext_reg <= 1'b0; + fb_if.imem_pc <= fb_if.reset_pc_val; + pc <= fb_if.reset_pc_val; + final_inst_store <= 32'd0; + reset_next <= 1'b0; + end else if (fb_if.reset_en) begin + buffer <= nextbuffer; + combine_reg <= combine; + waitnext_reg <= waitnext; + fb_if.imem_pc <= next_imem_pc; + pc <= fb_if.nextpc; + final_inst_store <= final_inst; + reset_next <= 1'b1; + end else if (fb_if.inst_arrived | fb_if.done_earlier) begin + buffer <= nextbuffer; + combine_reg <= combine; + waitnext_reg <= waitnext; + fb_if.imem_pc <= next_imem_pc; + pc <= fb_if.nextpc; + final_inst_store <= final_inst; + reset_next <= 1'b0; + end + end + + always_comb begin + next_imem_pc = fb_if.imem_pc; + fb_if.nextpc = pc; + nextbuffer = buffer; + combine = 1'b0; + waitnext = 1'b0; + final_inst = final_inst_store; + finished = 1'b0; + if (fb_if.inst_arrived & reset_next & (pc != fb_if.imem_pc)) begin // Handle Jump/Branch Condition when misaligned + next_imem_pc = fb_if.imem_pc + 4; + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + final_inst = {16'd0, fb_if.inst[31:16]}; + fb_if.nextpc = pc + 2; + finished = 1'b1; + end else begin + combine = 1; + nextbuffer = fb_if.inst[31:16]; +// nextpc = pc + 4; + finished = 1'b0; + end + end else if (fb_if.reset_en) begin + next_imem_pc = {fb_if.reset_pc[31:2], 2'b0}; // Always aligned + fb_if.nextpc = fb_if.reset_pc; // Can be misaligned + nextbuffer = 16'd0; + final_inst = 32'd0; + end else if (fb_if.inst_arrived & combine_reg) begin + final_inst = {fb_if.inst[15:0], buffer}; + nextbuffer = fb_if.inst[31:16]; + fb_if.nextpc = pc + 4; + finished = 1'b1; + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + waitnext = 1; + next_imem_pc = fb_if.imem_pc; + end else begin + combine = 1; + next_imem_pc = fb_if.imem_pc + 4; + end + end else if (fb_if.done_earlier | (fb_if.inst_arrived & waitnext_reg)) begin + final_inst = {16'b0, buffer}; + finished = 1'b1; + fb_if.nextpc = pc + 2; + next_imem_pc = fb_if.imem_pc + 4; + end else if (fb_if.inst[1:0] != 2'b11) begin // lower 16 bits are compressed + final_inst = fb_if.inst[15:0]; + nextbuffer = fb_if.inst[31:16]; + fb_if.nextpc = pc + 2; + finished = 1'b1; + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + waitnext = 1; + next_imem_pc = fb_if.imem_pc; + end else begin + combine = 1; + next_imem_pc = fb_if.imem_pc + 4; + end + end else if (fb_if.inst[1:0] == 2'b11) begin + final_inst = fb_if.inst; + next_imem_pc = fb_if.imem_pc + 4; + fb_if.nextpc = pc + 4; + finished = 1'b1; + end + end + + //logic [31:0] debug; + assign fb_if.result = final_inst; + //assign debug = fb_if.inst_arrived ? final_inst : final_inst_store; + //assign c_ena = fb_if.result[1:0] != 2'b11; + assign fb_if.done = fb_if.inst_arrived & finished; + +endmodule diff --git a/source_code/rv32c/rv32c_disabled.sv b/source_code/rv32c/rv32c_disabled.sv new file mode 100644 index 000000000..0ec15d1b0 --- /dev/null +++ b/source_code/rv32c/rv32c_disabled.sv @@ -0,0 +1,22 @@ +`include "rv32c_if.vh" +`include "fetch_buffer_if.vh" +`include "decompressor_if.vh" + +module rv32c_disabled +( + input logic clk, nrst, + rv32c_if.rv32c rv32cif + ); + + + assign rv32cif.done = '0; + assign rv32cif.done_earlier = '0; + assign rv32cif.nextpc = '0; + assign rv32cif.imem_pc = '0; + assign rv32cif.result = '0; + assign rv32cif.inst32 = '0; + assign rv32cif.c_ena = '0; + + assign rv32cif.rv32c_ena = '0; + +endmodule diff --git a/source_code/rv32c/rv32c_enabled.sv b/source_code/rv32c/rv32c_enabled.sv new file mode 100644 index 000000000..49fd221d7 --- /dev/null +++ b/source_code/rv32c/rv32c_enabled.sv @@ -0,0 +1,42 @@ +`include "rv32c_if.vh" +`include "fetch_buffer_if.vh" +`include "decompressor_if.vh" + +module rv32c_enabled +( + input logic clk, nrst, + rv32c_if.rv32c rv32cif + ); + //parameter RESET_PC = 32'h80000000; + // Fetch Buffer + fetch_buffer_if fb_if(); + fetch_buffer BUFFER + ( + .clk(clk), + .n_rst(nrst), + .fb_if(fb_if) + ); + assign fb_if.inst = rv32cif.inst; + assign fb_if.reset_en = rv32cif.reset_en; + assign fb_if.reset_pc = rv32cif.reset_pc; + assign fb_if.inst_arrived = rv32cif.inst_arrived; + assign fb_if.pc_update = rv32cif.pc_update; + assign fb_if.ex_busy = rv32cif.ex_busy; + assign fb_if.reset_pc_val = rv32cif.reset_pc_val; + assign rv32cif.done = fb_if.done; + assign rv32cif.done_earlier = fb_if.done_earlier & (rv32cif.halt == 0); + ///assign rv32cif.done_earlier_send = fb_if.done_earlier_send; + assign rv32cif.nextpc = fb_if.nextpc; + assign rv32cif.imem_pc = fb_if.imem_pc; + assign rv32cif.result = fb_if.result; + + // Decompressor + decompressor_if dcpr_if(); + decompressor DECOMPRESSOR (dcpr_if); + assign dcpr_if.inst16 = rv32cif.inst16; + assign rv32cif.inst32 = dcpr_if.inst32; + assign rv32cif.c_ena = dcpr_if.c_ena; + + assign rv32cif.rv32c_ena = 1; + +endmodule diff --git a/source_code/rv32c/rv32c_wrapper.sv b/source_code/rv32c/rv32c_wrapper.sv new file mode 100644 index 000000000..283eae09e --- /dev/null +++ b/source_code/rv32c/rv32c_wrapper.sv @@ -0,0 +1,16 @@ +`include "rv32c_if.vh" +`include "component_selection_defines.vh" + +module rv32c_wrapper( + input logic CLK, nRST, + rv32c_if.rv32c rv32cif +); + +generate + case(RV32C_ENABLED) + "disabled" : rv32c_disabled RV32C(.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); + "enabled" : rv32c_enabled RV32C(.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); + endcase +endgenerate + +endmodule diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index 3e9528e5e..a76582302 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -31,6 +31,7 @@ `include "tspp_fetch_execute_if.vh" `include "tspp_hazard_unit_if.vh" `include "core_interrupt_if.vh" +`include "rv32c_if.vh" module RISCVBusiness ( input logic CLK, nRST, @@ -54,6 +55,7 @@ module RISCVBusiness ( prv_pipeline_if prv_pipe_if(); cache_control_if cc_if(); sparce_pipeline_if sparce_if(); + rv32c_if rv32cif(); //interface instantiations tspp_fetch_execute_if fetch_ex_if(); @@ -76,6 +78,7 @@ module RISCVBusiness ( .sparce_if(sparce_if) ); */ + tspp_fetch_stage fetch_stage_i ( .CLK(CLK), .nRST(nRST), @@ -83,7 +86,8 @@ module RISCVBusiness ( .hazard_if(hazard_if), .predict_if(predict_if), .igen_bus_if(tspp_icache_gen_bus_if), - .sparce_if(sparce_if) + .sparce_if(sparce_if), + .rv32cif(rv32cif) ); tspp_execute_stage execute_stage_i ( @@ -97,7 +101,8 @@ module RISCVBusiness ( .halt(halt), .rm_if(rm_if), .cc_if(cc_if), - .sparce_if(sparce_if) + .sparce_if(sparce_if), + .rv32cif(rv32cif) ); tspp_hazard_unit hazard_unit_i ( @@ -163,6 +168,12 @@ module RISCVBusiness ( .sparce_if(sparce_if) ); + rv32c_wrapper rv32c ( + .CLK(CLK), + .nRST(nRST), + .rv32cif(rv32cif) + ); + // Instantiate the chosen bus interface generate diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index 7aa56033f..fe025b04a 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -26,6 +26,7 @@ `include "control_unit_if.vh" `include "rv32i_reg_file_if.vh" `include "risc_mgmt_if.vh" +`include "decompressor_if.vh" module control_unit ( diff --git a/source_code/wscript b/source_code/wscript index e8ae5157d..6d9dd6cc5 100644 --- a/source_code/wscript +++ b/source_code/wscript @@ -1,5 +1,5 @@ def configure(ctx): - rvb_srcs = ['caches', 'branch_predictors', 'bus_bridges', 'trackers', 'risc_mgmt', 'privs', 'pipelines', 'standard_core', 'ram', 'sparce'] + rvb_srcs = ['caches', 'branch_predictors', 'bus_bridges', 'trackers', 'risc_mgmt', 'privs', 'pipelines', 'standard_core', 'ram', 'sparce', 'rv32c'] ctx.SFFUnits.add('RISCVBusiness_packages', src_dir = ['packages'] ) diff --git a/sparce.yml b/sparce.yml index a012188de..a71cc82d7 100644 --- a/sparce.yml +++ b/sparce.yml @@ -19,6 +19,9 @@ microarch_params: # Sparisty Optimizations sparce_enabled : "enabled" + # RV32C + rv32c_enabled : "disabled" + # Halt infinite_loop_halts : "true" diff --git a/verification/asm-env/selfasm/test_macros.h b/verification/asm-env/selfasm/test_macros.h index 6bab5abb8..c225e17fb 100644 --- a/verification/asm-env/selfasm/test_macros.h +++ b/verification/asm-env/selfasm/test_macros.h @@ -595,6 +595,367 @@ test_ ## testnum: \ .double result; \ 1: +#----------------------------------------------------------------------- +# Tests compressed instructions (RV32C) +#----------------------------------------------------------------------- +#define TEST_RV32C_R_OP( testnum, inst, result, predefined_val, val1) \ + TEST_CASE( testnum, x13, result, \ + li x11, val1; \ + li x13, predefined_val; \ + inst x13, x11; \ + ) + +#define TEST_RV32C_RR_SRC12_EQ_DEST( testnum, inst, result, val1 ) \ + TEST_CASE( testnum, x11, result, \ + li x11, val1; \ + inst x11, x11; \ + ) + +#define TEST_RV32C_RR_DEST_BYPASS( testnum, nop_cycles, inst, result, predefined_val, val1) \ + TEST_CASE( testnum, x16, result, \ + li x14, 0; \ +1: li x11, val1; \ + li x13, predefined_val; \ + inst x13, x11; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x16, x13, 0; \ + addi x14, x14, 1; \ + li x15, 2; \ + bne x14, x15, 1b \ + ) + +#define TEST_RV32C_RR_SRC12_BYPASS( testnum, src1_nops, src2_nops, inst, result, predefined_val, val1) \ + TEST_CASE( testnum, x13, result, \ + li x14, 0; \ +1: li x11, val1; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x13, predefined_val; \ + TEST_INSERT_NOPS_ ## src2_nops \ + inst x13, x11; \ + addi x14, x14, 1; \ + li x15, 2; \ + bne x14, x15, 1b \ + ) + +#define TEST_RV32C_RR_SRC21_BYPASS( testnum, src1_nops, src2_nops, inst, result, predefined_val, val1) \ + TEST_CASE( testnum, x13, result, \ + li x14, 0; \ +1: li x13, predefined_val; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x11, val1; \ + TEST_INSERT_NOPS_ ## src2_nops \ + inst x13, x11; \ + addi x14, x14, 1; \ + li x15, 2; \ + bne x14, x15, 1b \ + ) + +#define TEST_RV32C_BR2_OP_TAKEN( testnum, inst, val1) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x12, val1; \ + inst x12, 2f; \ + bne x0, TESTNUM, fail; \ +1: bne x0, TESTNUM, 3f; \ +2: inst x12, 1b; \ + bne x0, TESTNUM, fail; \ +3: + +#define TEST_RV32C_BR2_OP_NOTTAKEN( testnum, inst, val1) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x9, val1; \ + inst x9, 1f; \ + bne x0, TESTNUM, 2f; \ +1: bne x0, TESTNUM, fail; \ +2: inst x9, 1b; \ +3: + +#define TEST_RV32C_BR2_SRC_BYPASS( testnum, src1_nops, inst, val1) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x6, 0; \ +1: li x14, val1; \ + TEST_INSERT_NOPS_ ## src1_nops \ + inst x14, fail; \ + addi x6, x6, 1; \ + li x5, 2; \ + bne x6, x5, 1b \ + +#define TEST_RV32C_JALR_SRC1_BYPASS( testnum, nop_cycles, inst ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x6, 2f; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x6; \ + bne x0, TESTNUM, fail; \ +2: addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_JAL_SRC1_BYPASS( testnum, nop_cycles, inst ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: TEST_INSERT_NOPS_ ## nop_cycles \ + inst 2f; \ + bne x0, TESTNUM, fail; \ +2: addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define SEXT_IMM_5(x) ((x) | (-(((x) >> 5) & 1) << 5)) + +#define TEST_RV32C_IMM_OP( testnum, inst, result, val1, imm ) \ + TEST_CASE( testnum, x10, result, \ + li x10, val1; \ + inst x10, SEXT_IMM_5(imm); \ + ) + +#define TEST_RV32C_IMM_DEST_BYPASS( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x6, result, \ + li x4, 0; \ +1: li x12, val1; \ + inst x12, SEXT_IMM_5(imm); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x12, 0; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RV32C_IMM_SRC1_BYPASS( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x14, result, \ + li x4, 0; \ +1: li x14, val1; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x14, SEXT_IMM_5(imm); \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define SEXT_IMM_9(x) ((x) | (-(((x) >> 9) & 1) << 9)) + +#define TEST_RV32C_IMM_OP_SP( testnum, inst, result, val1, imm ) \ + TEST_CASE( testnum, x2, result, \ + li x2, val1; \ + inst x2, SEXT_IMM_9(imm); \ + ) + +#define TEST_RV32C_IMM_DEST_BYPASS_SP( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x6, result, \ + li x4, 0; \ +1: li x2, val1; \ + inst x2, SEXT_IMM_9(imm); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x2, 0; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RV32C_IMM_SRC1_BYPASS_SP( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x2, result, \ + li x4, 0; \ +1: li x2, val1; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x2, SEXT_IMM_9(imm); \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define ZERO_IMM_9(x) ((x) | (0 << 10)) + +#define TEST_RV32C_IMM_OP_SP_REG( testnum, inst, result, val1, imm ) \ + TEST_CASE( testnum, x10, result, \ + li x2, val1; \ + inst x10, x2, ZERO_IMM_9(imm); \ + ) + +#define TEST_RV32C_IMM_DEST_BYPASS_SP_REG( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x6, result, \ + li x4, 0; \ +1: li x2, val1; \ + inst x11, x2, ZERO_IMM_9(imm); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x11, 0; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RV32C_IMM_SRC1_BYPASS_SP_REG( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x12, result, \ + li x4, 0; \ +1: li x2, val1; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x12, x2, ZERO_IMM_9(imm); \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RV32C_LI( testnum, inst, result, imm, shamt ) \ + TEST_CASE( testnum, x4, result, \ + inst x4, SEXT_IMM_5(imm); \ + sra x4, x4, shamt; \ + ) + +#define TEST_RV32C_LUI( testnum, inst, result, imm, shamt ) \ + TEST_CASE( testnum, x4, result, \ + inst x4, imm; \ + sra x4, x4, shamt; \ + ) + +#define TEST_RV32C_LD_OP( testnum, inst, result, offset, base ) \ + TEST_CASE( testnum, x13, result, \ + la x11, base; \ + inst x13, offset(x11); \ + ) + +#define TEST_RV32C_LD_DEST_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x11, base; \ + inst x13, offset(x11); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x13, 0; \ + li x29, result; \ + bne x6, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b; \ + +#define TEST_RV32C_LD_SRC1_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x11, base; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x13, offset(x11); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_ST_OP( testnum, load_inst, store_inst, result, offset, base ) \ + TEST_CASE( testnum, x13, result, \ + la x11, base; \ + li x12, result; \ + store_inst x12, offset(x11); \ + load_inst x13, offset(x11); \ + ) + +#define TEST_RV32C_ST_SRC12_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: li x11, result; \ + TEST_INSERT_NOPS_ ## src1_nops \ + la x12, base; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x11, offset(x12); \ + load_inst x13, offset(x12); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_ST_SRC21_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x12, base; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x11, result; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x11, offset(x12); \ + load_inst x13, offset(x12); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_LDSP_OP( testnum, inst, result, offset, base ) \ + TEST_CASE( testnum, x13, result, \ + la x2, base; \ + inst x13, offset(x2); \ + ) + +#define TEST_RV32C_LDSP_DEST_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x2, base; \ + inst x13, offset(x2); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x13, 0; \ + li x29, result; \ + bne x6, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b; \ + +#define TEST_RV32C_LDSP_SRC1_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x2, base; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x13, offset(x2); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_STSP_OP( testnum, load_inst, store_inst, result, offset, base ) \ + TEST_CASE( testnum, x13, result, \ + la x2, base; \ + li x12, result; \ + store_inst x12, offset(x2); \ + load_inst x13, offset(x2); \ + ) + +#define TEST_RV32C_STSP_SRC12_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: li x11, result; \ + TEST_INSERT_NOPS_ ## src1_nops \ + la x2, base; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x11, offset(x2); \ + load_inst x13, offset(x2); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_STSP_SRC21_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x2, base; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x11, result; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x11, offset(x2); \ + load_inst x13, offset(x2); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + #----------------------------------------------------------------------- # Pass and fail code (assumes test num is in TESTNUM) #----------------------------------------------------------------------- diff --git a/verification/self-tests/RV32C/c_add.S b/verification/self-tests/RV32C/c_add.S new file mode 100644 index 000000000..99cfcfd1d --- /dev/null +++ b/verification/self-tests/RV32C/c_add.S @@ -0,0 +1,81 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_add.S +#----------------------------------------------------------------------------- +# +# Test c_add instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.add, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RV32C_R_OP( 3, c.add, 0x00000002, 0x00000001, 0x00000001 ); + TEST_RV32C_R_OP( 4, c.add, 0x0000000a, 0x00000003, 0x00000007 ); + + TEST_RV32C_R_OP( 5, c.add, 0xffff8000, 0x00000000, 0xffff8000 ); + TEST_RV32C_R_OP( 6, c.add, 0x80000000, 0x80000000, 0x00000000 ); + TEST_RV32C_R_OP( 7, c.add, 0x7fff8000, 0x80000000, 0xffff8000 ); + + TEST_RV32C_R_OP( 8, c.add, 0x00007fff, 0x00000000, 0x00007fff ); + TEST_RV32C_R_OP( 9, c.add, 0x7fffffff, 0x7fffffff, 0x00000000 ); + TEST_RV32C_R_OP( 10, c.add, 0x80007ffe, 0x7fffffff, 0x00007fff ); + + TEST_RV32C_R_OP( 11, c.add, 0x80007fff, 0x80000000, 0x00007fff ); + TEST_RV32C_R_OP( 12, c.add, 0x7fff7fff, 0x7fffffff, 0xffff8000 ); + + TEST_RV32C_R_OP( 13, c.add, 0xffffffff, 0x00000000, 0xffffffff ); + TEST_RV32C_R_OP( 14, c.add, 0x00000000, 0xffffffff, 0x00000001 ); + TEST_RV32C_R_OP( 15, c.add, 0xfffffffe, 0xffffffff, 0xffffffff ); + + TEST_RV32C_R_OP( 16, c.add, 0x80000000, 0x00000001, 0x7fffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 17, c.add, 26, 13 ); + TEST_RV32C_RR_SRC12_EQ_DEST( 18, c.add, -200, -100 ); + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 19, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 20, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 21, 2, c.add, 26, 15, 11 ); + + TEST_RV32C_RR_SRC12_BYPASS( 22, 0, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 23, 0, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 24, 0, 2, c.add, 26, 15, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 25, 1, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 26, 1, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 27, 2, 0, c.add, 26, 15, 11 ); + + TEST_RV32C_RR_SRC21_BYPASS( 28, 0, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 29, 0, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 30, 0, 2, c.add, 26, 15, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 31, 1, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 32, 1, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 33, 2, 0, c.add, 26, 15, 11 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_addi.S b/verification/self-tests/RV32C/c_addi.S new file mode 100644 index 000000000..4239b7b77 --- /dev/null +++ b/verification/self-tests/RV32C/c_addi.S @@ -0,0 +1,63 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_addi.S +#----------------------------------------------------------------------------- +# +# Test c_addi instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 2, c.addi, 0x00000000, 0x00000000, 0x00 ); + TEST_RV32C_IMM_OP( 3, c.addi, 0x00000002, 0x00000001, 0x01 ); + TEST_RV32C_IMM_OP( 4, c.addi, 0x0000000a, 0x00000003, 0x07 ); + + TEST_RV32C_IMM_OP( 5, c.addi, 0x00000020, 0x00000001, 0x1f ); + TEST_RV32C_IMM_OP( 6, c.addi, 0x80000000, 0x80000000, 0x00 ); + TEST_RV32C_IMM_OP( 7, c.addi, 0x7fffffef, 0x80000000, 0x2f ); + + TEST_RV32C_IMM_OP( 8, c.addi, 0x1000000e, 0x0fffffff, 0x0f ); + TEST_RV32C_IMM_OP( 9, c.addi, 0x7fffffff, 0x7fffffff, 0x00 ); + TEST_RV32C_IMM_OP( 10, c.addi, 0x8000001e, 0x7fffffff, 0x1f ); + + TEST_RV32C_IMM_OP( 11, c.addi, 0x7fffffe2, 0x80000000, 0x22 ); + TEST_RV32C_IMM_OP( 12, c.addi, 0x7fffffdf, 0x7fffffff, 0x20 ); + + TEST_RV32C_IMM_OP( 13, c.addi, 0xffffffff, 0x00000000, 0x3f ); + TEST_RV32C_IMM_OP( 14, c.addi, 0x00000000, 0xffffffff, 0x01 ); + TEST_RV32C_IMM_OP( 15, c.addi, 0xfffffffe, 0xffffffff, 0x3f ); + + TEST_RV32C_IMM_OP( 16, c.addi, 0x80000000, 0x7fffffff, 0x01 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 17, 0, c.addi, 24, 13, 11 ); + TEST_RV32C_IMM_DEST_BYPASS( 18, 1, c.addi, 23, 13, 10 ); + TEST_RV32C_IMM_DEST_BYPASS( 19, 2, c.addi, 22, 13, 9 ); + + TEST_RV32C_IMM_SRC1_BYPASS( 20, 0, c.addi, 24, 13, 11 ); + TEST_RV32C_IMM_SRC1_BYPASS( 21, 1, c.addi, 23, 13, 10 ); + TEST_RV32C_IMM_SRC1_BYPASS( 22, 2, c.addi, 22, 13, 9 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_addi16sp.S b/verification/self-tests/RV32C/c_addi16sp.S new file mode 100644 index 000000000..a619a0075 --- /dev/null +++ b/verification/self-tests/RV32C/c_addi16sp.S @@ -0,0 +1,65 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_addi16sp.S +#----------------------------------------------------------------------------- +# +# Test c_addi16sp instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + # Immediate values should be multiple of 16 + + TEST_RV32C_IMM_OP_SP( 2, c.addi16sp, 0x00000010, 0x00000000, 0x010 ); + TEST_RV32C_IMM_OP_SP( 3, c.addi16sp, 0x00000111, 0x00000001, 0x110 ); + TEST_RV32C_IMM_OP_SP( 4, c.addi16sp, 0x000000aa, 0x0000003a, 0x070 ); + + TEST_RV32C_IMM_OP_SP( 5, c.addi16sp, 0x00000200, 0x00000010, 0x1f0 ); + TEST_RV32C_IMM_OP_SP( 6, c.addi16sp, 0x800000f0, 0x80000000, 0x0f0 ); + TEST_RV32C_IMM_OP_SP( 7, c.addi16sp, 0x7ffffef0, 0x80000000, 0x2f0 ); + + TEST_RV32C_IMM_OP_SP( 8, c.addi16sp, 0x000000e0, 0xfffffff0, 0x0f0 ); + TEST_RV32C_IMM_OP_SP( 9, c.addi16sp, 0x7ffffe9f, 0x7fffffff, 0x2a0 ); + TEST_RV32C_IMM_OP_SP( 10, c.addi16sp, 0x800001ef, 0x7fffffff, 0x1f0 ); + + TEST_RV32C_IMM_OP_SP( 11, c.addi16sp, 0x7ffffe20, 0x80000000, 0x220 ); + TEST_RV32C_IMM_OP_SP( 12, c.addi16sp, 0x7ffffdff, 0x7fffffff, 0x200 ); + + TEST_RV32C_IMM_OP_SP( 13, c.addi16sp, 0xfffffff0, 0x00000000, 0x3f0 ); + TEST_RV32C_IMM_OP_SP( 14, c.addi16sp, 0x00000000, 0xfffffff0, 0x010 ); + TEST_RV32C_IMM_OP_SP( 15, c.addi16sp, 0xffffffe0, 0xfffffff0, 0x3f0 ); + + TEST_RV32C_IMM_OP_SP( 16, c.addi16sp, 0x80000000, 0x7ffffff0, 0x010 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS_SP( 17, 0, c.addi16sp, 116, 100, 16 ); + TEST_RV32C_IMM_DEST_BYPASS_SP( 18, 1, c.addi16sp, 132, 100, 32 ); + TEST_RV32C_IMM_DEST_BYPASS_SP( 19, 2, c.addi16sp, 148, 100, 48 ); + + TEST_RV32C_IMM_SRC1_BYPASS_SP( 20, 0, c.addi16sp, 116, 100, 16 ); + TEST_RV32C_IMM_SRC1_BYPASS_SP( 21, 1, c.addi16sp, 132, 100, 32 ); + TEST_RV32C_IMM_SRC1_BYPASS_SP( 22, 2, c.addi16sp, 148, 100, 48 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_addi4spn.S b/verification/self-tests/RV32C/c_addi4spn.S new file mode 100644 index 000000000..18c7f4636 --- /dev/null +++ b/verification/self-tests/RV32C/c_addi4spn.S @@ -0,0 +1,66 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_addi4spn.S +#----------------------------------------------------------------------------- +# +# Test c_addi4spn instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + # Immediate values should be multiple of 4 + + TEST_RV32C_IMM_OP_SP_REG( 2, c.addi4spn, 0x00000010, 0x00000000, 0x010 ); + TEST_RV32C_IMM_OP_SP_REG( 3, c.addi4spn, 0x00000111, 0x00000001, 0x110 ); + TEST_RV32C_IMM_OP_SP_REG( 4, c.addi4spn, 0x000000aa, 0x0000003a, 0x070 ); + + TEST_RV32C_IMM_OP_SP_REG( 5, c.addi4spn, 0x00000200, 0x00000010, 0x1f0 ); + TEST_RV32C_IMM_OP_SP_REG( 6, c.addi4spn, 0x800000f0, 0x80000000, 0x0f0 ); + TEST_RV32C_IMM_OP_SP_REG( 7, c.addi4spn, 0x800002fc, 0x80000000, 0x2fc ); + + TEST_RV32C_IMM_OP_SP_REG( 8, c.addi4spn, 0x000000e0, 0xfffffff0, 0x0f0 ); + TEST_RV32C_IMM_OP_SP_REG( 9, c.addi4spn, 0x800002a7, 0x7fffffff, 0x2a8 ); + TEST_RV32C_IMM_OP_SP_REG( 10, c.addi4spn, 0x800001ef, 0x7fffffff, 0x1f0 ); + + TEST_RV32C_IMM_OP_SP_REG( 11, c.addi4spn, 0x800003fc, 0x80000000, 0x3fc ); + TEST_RV32C_IMM_OP_SP_REG( 12, c.addi4spn, 0x100003fb, 0x0fffffff, 0x3fc ); + + TEST_RV32C_IMM_OP_SP_REG( 13, c.addi4spn, 0x000003f0, 0x00000000, 0x3f0 ); + TEST_RV32C_IMM_OP_SP_REG( 14, c.addi4spn, 0x00000000, 0xfffffff0, 0x010 ); + TEST_RV32C_IMM_OP_SP_REG( 15, c.addi4spn, 0x000003e0, 0xfffffff0, 0x3f0 ); + + TEST_RV32C_IMM_OP_SP_REG( 16, c.addi4spn, 0x80000000, 0x7ffffff0, 0x010 ); + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS_SP_REG( 17, 0, c.addi4spn, 154, 150, 4 ); + TEST_RV32C_IMM_DEST_BYPASS_SP_REG( 18, 1, c.addi4spn, 158, 150, 8 ); + TEST_RV32C_IMM_DEST_BYPASS_SP_REG( 19, 2, c.addi4spn, 162, 150, 12 ); + + TEST_RV32C_IMM_SRC1_BYPASS_SP_REG( 20, 0, c.addi4spn, 154, 150, 4 ); + TEST_RV32C_IMM_SRC1_BYPASS_SP_REG( 21, 1, c.addi4spn, 158, 150, 8 ); + TEST_RV32C_IMM_SRC1_BYPASS_SP_REG( 22, 2, c.addi4spn, 162, 150, 12 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_and.S b/verification/self-tests/RV32C/c_and.S new file mode 100644 index 000000000..14adee5f7 --- /dev/null +++ b/verification/self-tests/RV32C/c_and.S @@ -0,0 +1,64 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_and.S +#----------------------------------------------------------------------------- +# +# Test c_and instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 3, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_R_OP( 4, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 5, c.and, 0xf000f000, 0xf00ff00f, 0xf0f0f0f0 ); + TEST_RV32C_R_OP( 6, c.and, 0x00000000, 0x00000000, 0xf0f0f0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 7, c.and, 0xff00ff00, 0xff00ff00 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 8, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_DEST_BYPASS( 9, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_DEST_BYPASS( 10, 2, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC12_BYPASS( 11, 0, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 12, 0, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 13, 0, 2, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 14, 1, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 15, 1, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 16, 2, 0, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC21_BYPASS( 17, 0, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 18, 0, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 19, 0, 2, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 20, 1, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 21, 1, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 22, 2, 0, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_andi.S b/verification/self-tests/RV32C/c_andi.S new file mode 100644 index 000000000..3d00e2f6c --- /dev/null +++ b/verification/self-tests/RV32C/c_andi.S @@ -0,0 +1,47 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_andi.S +#----------------------------------------------------------------------------- +# +# Test c_andi instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 2, c.andi, 0xff00ff00, 0xff00ff00, 0x3f ); + TEST_RV32C_IMM_OP( 3, c.andi, 0x00000010, 0x0ff00ff0, 0x10 ); + TEST_RV32C_IMM_OP( 4, c.andi, 0x0000000f, 0x00ff00ff, 0x0f ); + TEST_RV32C_IMM_OP( 5, c.andi, 0x00000000, 0xf00ff00f, 0x00 ); + TEST_RV32C_IMM_OP( 6, c.andi, 0x00000000, 0x00000000, 0x20 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 7, 0, c.andi, 0xfff00ff0, 0xfff00ff0, 0x3f ); + TEST_RV32C_IMM_DEST_BYPASS( 8, 1, c.andi, 0xe0ff00f0, 0xe0ff00ff, 0x30 ); + TEST_RV32C_IMM_DEST_BYPASS( 9, 2, c.andi, 0x0000000f, 0xf00ff00f, 0x0f ); + + TEST_RV32C_IMM_SRC1_BYPASS( 10, 0, c.andi, 0x0ff00ff0, 0x0ff00ff0, 0x3f ); + TEST_RV32C_IMM_SRC1_BYPASS( 11, 1, c.andi, 0x00ff00f0, 0x00ff00ff, 0x30 ); + TEST_RV32C_IMM_SRC1_BYPASS( 12, 2, c.andi, 0x0000000f, 0xf00ff00f, 0x0f ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_beqz.S b/verification/self-tests/RV32C/c_beqz.S new file mode 100644 index 000000000..5a7ad379c --- /dev/null +++ b/verification/self-tests/RV32C/c_beqz.S @@ -0,0 +1,60 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_beqz.S +#----------------------------------------------------------------------------- +# +# Test c_beqz instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_RV32C_BR2_OP_TAKEN( 3, c.beqz, 0); + + TEST_RV32C_BR2_OP_NOTTAKEN( 4, c.beqz, 1); + TEST_RV32C_BR2_OP_NOTTAKEN( 5, c.beqz, -1); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_BR2_SRC_BYPASS( 6, 0, c.beqz, -1); + TEST_RV32C_BR2_SRC_BYPASS( 7, 1, c.beqz, -1); + TEST_RV32C_BR2_SRC_BYPASS( 8, 2, c.beqz, -1); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 9, x8, 3, \ + li x8, 1; \ + li x15, 0; + c.beqz x15, 1f; \ + addi x8, x8, 1; \ + addi x8, x8, 1; \ + addi x8, x8, 1; \ + addi x8, x8, 1; \ +1: addi x8, x8, 1; \ + addi x8, x8, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_bnez.S b/verification/self-tests/RV32C/c_bnez.S new file mode 100644 index 000000000..3f705516a --- /dev/null +++ b/verification/self-tests/RV32C/c_bnez.S @@ -0,0 +1,59 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_bnez.S +#----------------------------------------------------------------------------- +# +# Test c_bnez instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_RV32C_BR2_OP_TAKEN( 3, c.bnez, 1); + TEST_RV32C_BR2_OP_TAKEN( 4, c.bnez, -1); + + TEST_RV32C_BR2_OP_NOTTAKEN( 5, c.bnez, 0); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_BR2_SRC_BYPASS( 6, 0, c.bnez, 0); + TEST_RV32C_BR2_SRC_BYPASS( 7, 1, c.bnez, 0); + TEST_RV32C_BR2_SRC_BYPASS( 8, 2, c.bnez, 0); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 9, x11, 3, \ + li x11, 1; \ + c.bnez x11, 1f; \ + addi x11, x11, 1; \ + addi x11, x11, 1; \ + addi x11, x11, 1; \ + addi x11, x11, 1; \ +1: addi x11, x11, 1; \ + addi x11, x11, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_j.S b/verification/self-tests/RV32C/c_j.S new file mode 100644 index 000000000..cb09ed2df --- /dev/null +++ b/verification/self-tests/RV32C/c_j.S @@ -0,0 +1,86 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_j.S +#----------------------------------------------------------------------------- +# +# Test c_j instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li x31, 0 + +linkaddr_2: + c.j target_2 + nop + nop + + j fail + +target_2: + la x3, linkaddr_2 + addi x3, x3, 2 + beq x3, x1, fail + + #------------------------------------------------------------- + # Test 3: Check r0 target and that r31 is not modified + #------------------------------------------------------------- + +test_3: + li TESTNUM, 3 + li x31, 0 + +linkaddr_3: + c.j target_3 + nop + + j fail + +target_3: + bne x31, x0, fail + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_JAL_SRC1_BYPASS( 4, 0, c.j ); + TEST_RV32C_JAL_SRC1_BYPASS( 5, 1, c.j ); + TEST_RV32C_JAL_SRC1_BYPASS( 6, 2, c.j ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 7, x3, 3, \ + li x3, 1; \ + c.j 1f; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ +1: addi x3, x3, 1; \ + addi x3, x3, 1; \ + ) + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_jal.S b/verification/self-tests/RV32C/c_jal.S new file mode 100644 index 000000000..458c3039d --- /dev/null +++ b/verification/self-tests/RV32C/c_jal.S @@ -0,0 +1,86 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_jal.S +#----------------------------------------------------------------------------- +# +# Test c_jal instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li x31, 0 + +linkaddr_2: + c.jal target_2 + nop + nop + + j fail + +target_2: + la x3, linkaddr_2 + addi x3, x3, 2 + bne x3, x1, fail + + #------------------------------------------------------------- + # Test 3: Check r0 target and that r31 is not modified + #------------------------------------------------------------- + +test_3: + li TESTNUM, 3 + li x31, 0 + +linkaddr_3: + c.jal target_3 + nop + + j fail + +target_3: + bne x31, x0, fail + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_JAL_SRC1_BYPASS( 4, 0, c.jal ); + TEST_RV32C_JAL_SRC1_BYPASS( 5, 1, c.jal ); + TEST_RV32C_JAL_SRC1_BYPASS( 6, 2, c.jal ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 7, x3, 3, \ + li x3, 1; \ + c.jal 1f; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ +1: addi x3, x3, 1; \ + addi x3, x3, 1; \ + ) + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_jalr.S b/verification/self-tests/RV32C/c_jalr.S new file mode 100644 index 000000000..e2d41ba54 --- /dev/null +++ b/verification/self-tests/RV32C/c_jalr.S @@ -0,0 +1,89 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_jalr.S +#----------------------------------------------------------------------------- +# +# Test c_jalr instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li x31, 0 + la x2, target_2 + +linkaddr_2: + c.jalr x2 + nop + nop + + j fail + +target_2: + la x3, linkaddr_2 + addi x3, x3, 2 + bne x3, x1, fail + + #------------------------------------------------------------- + # Test 3: Check r0 target and that r31 is not modified + #------------------------------------------------------------- + +test_3: + li TESTNUM, 3 + li x31, 0 + la x3, target_3 + +linkaddr_3: + c.jalr x3 + nop + + j fail + +target_3: + bne x31, x0, fail + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_JALR_SRC1_BYPASS( 4, 0, c.jalr ); + TEST_RV32C_JALR_SRC1_BYPASS( 5, 1, c.jalr ); + TEST_RV32C_JALR_SRC1_BYPASS( 6, 2, c.jalr ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 7, x3, 3, \ + li x3, 1; \ + la x2, 1f; + c.jalr x2; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ +1: addi x3, x3, 1; \ + addi x3, x3, 1; \ + ) + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_jr.S b/verification/self-tests/RV32C/c_jr.S new file mode 100644 index 000000000..f4616e2ef --- /dev/null +++ b/verification/self-tests/RV32C/c_jr.S @@ -0,0 +1,89 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_jr.S +#----------------------------------------------------------------------------- +# +# Test c_jr instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li x31, 0 + la x2, target_2 + +linkaddr_2: + c.jr x2 + nop + nop + + j fail + +target_2: + la x3, linkaddr_2 + addi x3, x3, 2 + beq x3, x1, fail + + #------------------------------------------------------------- + # Test 3: Check r0 target and that r31 is not modified + #------------------------------------------------------------- + +test_3: + li TESTNUM, 3 + li x31, 0 + la x3, target_3 + +linkaddr_3: + c.jr x3 + nop + + j fail + +target_3: + bne x31, x0, fail + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_JALR_SRC1_BYPASS( 4, 0, c.jr ); + TEST_RV32C_JALR_SRC1_BYPASS( 5, 1, c.jr ); + TEST_RV32C_JALR_SRC1_BYPASS( 6, 2, c.jr ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 7, x3, 3, \ + li x3, 1; \ + la x2, 1f; + c.jr x2; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ +1: addi x3, x3, 1; \ + addi x3, x3, 1; \ + ) + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_li.S b/verification/self-tests/RV32C/c_li.S new file mode 100644 index 000000000..3db5b5e71 --- /dev/null +++ b/verification/self-tests/RV32C/c_li.S @@ -0,0 +1,34 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_li.S +#----------------------------------------------------------------------------- +# +# Test c_li instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_LI(2, c.li, 0xffffffff, 0x3f, 0); + TEST_RV32C_LI(3, c.li, 0x0000001f, 0x1f, 0); + TEST_RV32C_LI(4, c.li, 0x00000001, 0x10, 4); + TEST_RV32C_LI(5, c.li, 0xffffffff, 0x20, 20); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_lui.S b/verification/self-tests/RV32C/c_lui.S new file mode 100644 index 000000000..e5db357b2 --- /dev/null +++ b/verification/self-tests/RV32C/c_lui.S @@ -0,0 +1,34 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_lui.S +#----------------------------------------------------------------------------- +# +# Test c_lui instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_LUI(2, c.lui, 0x0001f000, 0x1f, 0); + TEST_RV32C_LUI(3, c.lui, 0xfffff000, 0xfffff, 0); + TEST_RV32C_LUI(4, c.lui, 0xffffe000, 0xfffe0, 4); + TEST_RV32C_LUI(5, c.lui, 0xfffffff0, 0xffff0, 12); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_lw.S b/verification/self-tests/RV32C/c_lw.S new file mode 100644 index 000000000..152b88925 --- /dev/null +++ b/verification/self-tests/RV32C/c_lw.S @@ -0,0 +1,77 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_lw.S +#----------------------------------------------------------------------------- +# +# Test c_lw instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_LD_OP( 2, c.lw, 0x00ff00ff, 0, tdat ); + TEST_RV32C_LD_OP( 3, c.lw, 0xff00ff00, 4, tdat ); + TEST_RV32C_LD_OP( 4, c.lw, 0x0ff00ff0, 8, tdat ); + TEST_RV32C_LD_OP( 5, c.lw, 0xf00ff00f, 12, tdat ); + + # Test with a negative base + + TEST_CASE( 6, x13, 0x00ff00ff, \ + la x11, tdat; \ + addi x11, x11, -32; \ + c.lw x13, 32(x11); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_LD_DEST_BYPASS( 7, 0, c.lw, 0x0ff00ff0, 4, tdat2 ); + TEST_RV32C_LD_DEST_BYPASS( 8, 1, c.lw, 0xf00ff00f, 4, tdat3 ); + TEST_RV32C_LD_DEST_BYPASS( 9, 2, c.lw, 0xff00ff00, 4, tdat1 ); + + TEST_RV32C_LD_SRC1_BYPASS( 10, 0, c.lw, 0x0ff00ff0, 4, tdat2 ); + TEST_RV32C_LD_SRC1_BYPASS( 11, 1, c.lw, 0xf00ff00f, 4, tdat3 ); + TEST_RV32C_LD_SRC1_BYPASS( 12, 2, c.lw, 0xff00ff00, 4, tdat1 ); + + #------------------------------------------------------------- + # Test write-after-write hazard + #------------------------------------------------------------- + + TEST_CASE( 13, x12, 2, \ + la x13, tdat; \ + c.lw x12, 0(x13); \ + li x12, 2; \ + ) + + TEST_CASE( 14, x12, 2, \ + la x13, tdat; \ + c.lw x12, 0(x13); \ + nop; \ + li x12, 2; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0x00ff00ff +tdat2: .word 0xff00ff00 +tdat3: .word 0x0ff00ff0 +tdat4: .word 0xf00ff00f + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_lwsp.S b/verification/self-tests/RV32C/c_lwsp.S new file mode 100644 index 000000000..42642702c --- /dev/null +++ b/verification/self-tests/RV32C/c_lwsp.S @@ -0,0 +1,77 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_lwsp.S +#----------------------------------------------------------------------------- +# +# Test c_lwsp instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_LDSP_OP( 2, c.lwsp, 0x00ff00ff, 0, tdat ); + TEST_RV32C_LDSP_OP( 3, c.lwsp, 0xff00ff00, 4, tdat ); + TEST_RV32C_LDSP_OP( 4, c.lwsp, 0x0ff00ff0, 8, tdat ); + TEST_RV32C_LDSP_OP( 5, c.lwsp, 0xf00ff00f, 12, tdat ); + + # Test with a negative base + + TEST_CASE( 6, x13, 0x00ff00ff, \ + la x2, tdat; \ + addi x2, x2, -32; \ + c.lwsp x13, 32(x2); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_LDSP_DEST_BYPASS( 7, 0, c.lwsp, 0x0ff00ff0, 4, tdat2 ); + TEST_RV32C_LDSP_DEST_BYPASS( 8, 1, c.lwsp, 0xf00ff00f, 4, tdat3 ); + TEST_RV32C_LDSP_DEST_BYPASS( 9, 2, c.lwsp, 0xff00ff00, 4, tdat1 ); + + TEST_RV32C_LDSP_SRC1_BYPASS( 10, 0, c.lwsp, 0x0ff00ff0, 4, tdat2 ); + TEST_RV32C_LDSP_SRC1_BYPASS( 11, 1, c.lwsp, 0xf00ff00f, 4, tdat3 ); + TEST_RV32C_LDSP_SRC1_BYPASS( 12, 2, c.lwsp, 0xff00ff00, 4, tdat1 ); + + #------------------------------------------------------------- + # Test write-after-write hazard + #------------------------------------------------------------- + + TEST_CASE( 13, x12, 2, \ + la x2, tdat; \ + c.lwsp x12, 0(x2); \ + li x12, 2; \ + ) + + TEST_CASE( 14, x12, 2, \ + la x2, tdat; \ + c.lwsp x12, 0(x2); \ + nop; \ + li x12, 2; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0x00ff00ff +tdat2: .word 0xff00ff00 +tdat3: .word 0x0ff00ff0 +tdat4: .word 0xf00ff00f + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_mv.S b/verification/self-tests/RV32C/c_mv.S new file mode 100644 index 000000000..0ddd8023d --- /dev/null +++ b/verification/self-tests/RV32C/c_mv.S @@ -0,0 +1,85 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_mv.S +#----------------------------------------------------------------------------- +# +# Test c_mv instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + # 4th argument has no effect + + TEST_RV32C_R_OP( 2, c.mv, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RV32C_R_OP( 3, c.mv, 0x00000002, 0x00000000, 0x00000002 ); + TEST_RV32C_R_OP( 4, c.mv, 0x0000000a, 0x00000000, 0x0000000a ); + + TEST_RV32C_R_OP( 5, c.mv, 0xffff8000, 0x00000000, 0xffff8000 ); + TEST_RV32C_R_OP( 6, c.mv, 0x80000000, 0x00000000, 0x80000000 ); + TEST_RV32C_R_OP( 7, c.mv, 0x7fff8000, 0x00000000, 0x7fff8000 ); + + TEST_RV32C_R_OP( 8, c.mv, 0x00007fff, 0x00000000, 0x00007fff ); + TEST_RV32C_R_OP( 9, c.mv, 0x7fffffff, 0x00000000, 0x7fffffff ); + TEST_RV32C_R_OP( 10, c.mv, 0x80007ffe, 0x00000000, 0x80007ffe ); + + TEST_RV32C_R_OP( 11, c.mv, 0x80007fff, 0x00000000, 0x80007fff ); + TEST_RV32C_R_OP( 12, c.mv, 0x7fff7fff, 0x00000000, 0x7fff7fff ); + + TEST_RV32C_R_OP( 13, c.mv, 0xffffffff, 0x00000000, 0xffffffff ); + TEST_RV32C_R_OP( 14, c.mv, 0x00000001, 0x00000000, 0x00000001 ); + TEST_RV32C_R_OP( 15, c.mv, 0xfffffffe, 0x00000000, 0xfffffffe ); + + TEST_RV32C_R_OP( 16, c.mv, 0x80000000, 0x00000000, 0x80000000 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 17, c.mv, 13, 13 ); + TEST_RV32C_RR_SRC12_EQ_DEST( 18, c.mv, -100, -100 ); + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + # 5th argument has no effect + + TEST_RV32C_RR_DEST_BYPASS( 19, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 20, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_DEST_BYPASS( 21, 2, c.mv, 13, 0, 13 ); + + TEST_RV32C_RR_SRC12_BYPASS( 22, 0, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 23, 0, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_SRC12_BYPASS( 24, 0, 2, c.mv, 13, 0, 13 ); + TEST_RV32C_RR_SRC12_BYPASS( 25, 1, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 26, 1, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_SRC12_BYPASS( 27, 2, 0, c.mv, 13, 0, 13 ); + + TEST_RV32C_RR_SRC21_BYPASS( 28, 0, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 29, 0, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_SRC21_BYPASS( 30, 0, 2, c.mv, 13, 0, 13 ); + TEST_RV32C_RR_SRC21_BYPASS( 31, 1, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 32, 1, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_SRC21_BYPASS( 33, 2, 0, c.mv, 13, 0, 13 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_or.S b/verification/self-tests/RV32C/c_or.S new file mode 100644 index 000000000..3e97e1482 --- /dev/null +++ b/verification/self-tests/RV32C/c_or.S @@ -0,0 +1,62 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_or.S +#----------------------------------------------------------------------------- +# +# Test c_or instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 3, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_R_OP( 4, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 5, c.or, 0xf0fff0ff, 0xf00ff00f, 0xf0f0f0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 6, c.or, 0xff00ff00, 0xff00ff00 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 7, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_DEST_BYPASS( 8, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_DEST_BYPASS( 9, 2, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC12_BYPASS( 10, 0, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 11, 0, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 12, 0, 2, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 13, 1, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 14, 1, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 15, 2, 0, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC21_BYPASS( 16, 0, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 17, 0, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 18, 0, 2, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 19, 1, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 20, 1, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 21, 2, 0, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_slli.S b/verification/self-tests/RV32C/c_slli.S new file mode 100644 index 000000000..68369f879 --- /dev/null +++ b/verification/self-tests/RV32C/c_slli.S @@ -0,0 +1,60 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_slli.S +#----------------------------------------------------------------------------- +# +# Test c_slli instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 2, c.slli, 0x00000000, 0x00000000, 11 ); + TEST_RV32C_IMM_OP( 3, c.slli, 0x00000002, 0x00000001, 1 ); + TEST_RV32C_IMM_OP( 4, c.slli, 0x00000080, 0x00000001, 7 ); + TEST_RV32C_IMM_OP( 5, c.slli, 0x00004000, 0x00000001, 14 ); + TEST_RV32C_IMM_OP( 6, c.slli, 0x80000000, 0x00000001, 31 ); + + TEST_RV32C_IMM_OP( 7, c.slli, 0x00000000, 0x00000000, 23 ); + TEST_RV32C_IMM_OP( 8, c.slli, 0xfffffffe, 0xffffffff, 1 ); + TEST_RV32C_IMM_OP( 9, c.slli, 0xffffff80, 0xffffffff, 7 ); + TEST_RV32C_IMM_OP( 10, c.slli, 0xffffc000, 0xffffffff, 14 ); + TEST_RV32C_IMM_OP( 11, c.slli, 0x80000000, 0xffffffff, 31 ); + + TEST_RV32C_IMM_OP( 12, c.slli, 0x00000000, 0x00000000, 30 ); + TEST_RV32C_IMM_OP( 13, c.slli, 0x42424242, 0x21212121, 1 ); + TEST_RV32C_IMM_OP( 14, c.slli, 0x90909080, 0x21212121, 7 ); + TEST_RV32C_IMM_OP( 15, c.slli, 0x48484000, 0x21212121, 14 ); + TEST_RV32C_IMM_OP( 16, c.slli, 0x80000000, 0x21212121, 31 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 17, 0, c.slli, 0x00000080, 0x00000001, 7 ); + TEST_RV32C_IMM_DEST_BYPASS( 18, 1, c.slli, 0x00004000, 0x00000001, 14 ); + TEST_RV32C_IMM_DEST_BYPASS( 19, 2, c.slli, 0x80000000, 0x00000001, 31 ); + + TEST_RV32C_IMM_SRC1_BYPASS( 20, 0, c.slli, 0x00000080, 0x00000001, 7 ); + TEST_RV32C_IMM_SRC1_BYPASS( 21, 1, c.slli, 0x00004000, 0x00000001, 14 ); + TEST_RV32C_IMM_SRC1_BYPASS( 22, 2, c.slli, 0x80000000, 0x00000001, 31 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_srai.S b/verification/self-tests/RV32C/c_srai.S new file mode 100644 index 000000000..0155bc141 --- /dev/null +++ b/verification/self-tests/RV32C/c_srai.S @@ -0,0 +1,60 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_srai.S +#----------------------------------------------------------------------------- +# +# Test c_srai instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 2, c.srai, 0x00000000, 0x00000000, 16 ); + TEST_RV32C_IMM_OP( 3, c.srai, 0xc0000000, 0x80000000, 1 ); + TEST_RV32C_IMM_OP( 4, c.srai, 0xff000000, 0x80000000, 7 ); + TEST_RV32C_IMM_OP( 5, c.srai, 0xfffe0000, 0x80000000, 14 ); + TEST_RV32C_IMM_OP( 6, c.srai, 0xffffffff, 0x80000001, 31 ); + + TEST_RV32C_IMM_OP( 7, c.srai, 0x00007fff, 0x7fffffff, 16 ); + TEST_RV32C_IMM_OP( 8, c.srai, 0x3fffffff, 0x7fffffff, 1 ); + TEST_RV32C_IMM_OP( 9, c.srai, 0x00ffffff, 0x7fffffff, 7 ); + TEST_RV32C_IMM_OP( 10, c.srai, 0x0001ffff, 0x7fffffff, 14 ); + TEST_RV32C_IMM_OP( 11, c.srai, 0x00000000, 0x7fffffff, 31 ); + + TEST_RV32C_IMM_OP( 12, c.srai, 0xffff8181, 0x81818181, 16 ); + TEST_RV32C_IMM_OP( 13, c.srai, 0xc0c0c0c0, 0x81818181, 1 ); + TEST_RV32C_IMM_OP( 14, c.srai, 0xff030303, 0x81818181, 7 ); + TEST_RV32C_IMM_OP( 15, c.srai, 0xfffe0606, 0x81818181, 14 ); + TEST_RV32C_IMM_OP( 16, c.srai, 0xffffffff, 0x81818181, 31 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 17, 0, c.srai, 0xff000000, 0x80000000, 7 ); + TEST_RV32C_IMM_DEST_BYPASS( 18, 1, c.srai, 0xfffe0000, 0x80000000, 14 ); + TEST_RV32C_IMM_DEST_BYPASS( 19, 2, c.srai, 0xffffffff, 0x80000001, 31 ); + + TEST_RV32C_IMM_SRC1_BYPASS( 20, 0, c.srai, 0xff000000, 0x80000000, 7 ); + TEST_RV32C_IMM_SRC1_BYPASS( 21, 1, c.srai, 0xfffe0000, 0x80000000, 14 ); + TEST_RV32C_IMM_SRC1_BYPASS( 22, 2, c.srai, 0xffffffff, 0x80000001, 31 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_srli.S b/verification/self-tests/RV32C/c_srli.S new file mode 100644 index 000000000..aa82f94ea --- /dev/null +++ b/verification/self-tests/RV32C/c_srli.S @@ -0,0 +1,59 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_srli.S +#----------------------------------------------------------------------------- +# +# Test c_srli instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 3, c.srli, 0x7fffc000, 0xffff8000, 1 ); + TEST_RV32C_IMM_OP( 4, c.srli, 0x01ffff00, 0xffff8000, 7 ); + TEST_RV32C_IMM_OP( 5, c.srli, 0x0003fffe, 0xffff8000, 14 ); + TEST_RV32C_IMM_OP( 6, c.srli, 0x0001ffff, 0xffff8001, 15 ); + + TEST_RV32C_IMM_OP( 7, c.srli, 0x00000000, 0x00000000, 17 ); + TEST_RV32C_IMM_OP( 8, c.srli, 0x7fffffff, 0xffffffff, 1 ); + TEST_RV32C_IMM_OP( 9, c.srli, 0x01ffffff, 0xffffffff, 7 ); + TEST_RV32C_IMM_OP( 10, c.srli, 0x0003ffff, 0xffffffff, 14 ); + TEST_RV32C_IMM_OP( 11, c.srli, 0x00000001, 0xffffffff, 31 ); + + TEST_RV32C_IMM_OP( 12, c.srli, 0x00000000, 0x00000000, 30 ); + TEST_RV32C_IMM_OP( 13, c.srli, 0x10909090, 0x21212121, 1 ); + TEST_RV32C_IMM_OP( 14, c.srli, 0x00424242, 0x21212121, 7 ); + TEST_RV32C_IMM_OP( 15, c.srli, 0x00008484, 0x21212121, 14 ); + TEST_RV32C_IMM_OP( 16, c.srli, 0x00000000, 0x21212121, 31 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 17, 0, c.srli, 0x7fffc000, 0xffff8000, 1 ); + TEST_RV32C_IMM_DEST_BYPASS( 18, 1, c.srli, 0x0003fffe, 0xffff8000, 14 ); + TEST_RV32C_IMM_DEST_BYPASS( 19, 2, c.srli, 0x0001ffff, 0xffff8000, 15 ); + + TEST_RV32C_IMM_SRC1_BYPASS( 20, 0, c.srli, 0x7fffc000, 0xffff8000, 1 ); + TEST_RV32C_IMM_SRC1_BYPASS( 21, 1, c.srli, 0x0003fffe, 0xffff8000, 14 ); + TEST_RV32C_IMM_SRC1_BYPASS( 22, 2, c.srli, 0x0001ffff, 0xffff8000, 15 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_sub.S b/verification/self-tests/RV32C/c_sub.S new file mode 100644 index 000000000..8e467caf4 --- /dev/null +++ b/verification/self-tests/RV32C/c_sub.S @@ -0,0 +1,77 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_sub.S +#----------------------------------------------------------------------------- +# +# Test c_sub instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.sub, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RV32C_R_OP( 3, c.sub, 0x00000000, 0x00000001, 0x00000001 ); + TEST_RV32C_R_OP( 4, c.sub, 0xfffffffc, 0x00000003, 0x00000007 ); + + TEST_RV32C_R_OP( 5, c.sub, 0x00008000, 0x00000000, 0xffff8000 ); + TEST_RV32C_R_OP( 6, c.sub, 0x80000000, 0x80000000, 0x00000000 ); + TEST_RV32C_R_OP( 7, c.sub, 0x80008000, 0x80000000, 0xffff8000 ); + + TEST_RV32C_R_OP( 8, c.sub, 0xffff8001, 0x00000000, 0x00007fff ); + TEST_RV32C_R_OP( 9, c.sub, 0x7fffffff, 0x7fffffff, 0x00000000 ); + TEST_RV32C_R_OP( 10, c.sub, 0x7fff8000, 0x7fffffff, 0x00007fff ); + + TEST_RV32C_R_OP( 11, c.sub, 0x7fff8001, 0x80000000, 0x00007fff ); + TEST_RV32C_R_OP( 12, c.sub, 0x80007fff, 0x7fffffff, 0xffff8000 ); + + TEST_RV32C_R_OP( 13, c.sub, 0x00000001, 0x00000000, 0xffffffff ); + TEST_RV32C_R_OP( 14, c.sub, 0xfffffffe, 0xffffffff, 0x00000001 ); + TEST_RV32C_R_OP( 15, c.sub, 0x00000000, 0xffffffff, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 16, c.sub, 0, 13 ); + TEST_RV32C_RR_SRC12_EQ_DEST( 17, c.sub, 0, -5 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 18, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 19, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 20, 2, c.sub, 4, 15, 11 ); + + TEST_RV32C_RR_SRC12_BYPASS( 21, 0, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 22, 0, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 23, 0, 2, c.sub, 4, 15, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 24, 1, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 25, 1, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 26, 2, 0, c.sub, 4, 15, 11 ); + + TEST_RV32C_RR_SRC21_BYPASS( 27, 0, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 28, 0, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 29, 0, 2, c.sub, 4, 15, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 30, 1, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 31, 1, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 32, 2, 0, c.sub, 4, 15, 11 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_sw.S b/verification/self-tests/RV32C/c_sw.S new file mode 100644 index 000000000..8df5f2219 --- /dev/null +++ b/verification/self-tests/RV32C/c_sw.S @@ -0,0 +1,75 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_sw.S +#----------------------------------------------------------------------------- +# +# Test c_sw instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_ST_OP( 2, c.lw, c.sw, 0x00aa00aa, 0, tdat ); + TEST_RV32C_ST_OP( 3, c.lw, c.sw, 0xaa00aa00, 4, tdat ); + TEST_RV32C_ST_OP( 4, c.lw, c.sw, 0x0aa00aa0, 8, tdat ); + TEST_RV32C_ST_OP( 5, c.lw, c.sw, 0xa00aa00a, 12, tdat ); + + # Test with a negative base + + TEST_CASE( 6, x13, 0x12345678, \ + la x11, tdat9; \ + li x12, 0x12345678; \ + addi x14, x11, -32; \ + c.sw x12, 32(x14); \ + c.lw x13, 0(x11); \ + ) + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_ST_SRC12_BYPASS( 7, 0, 0, c.lw, c.sw, 0xaabbccdd, 0, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 8, 0, 1, c.lw, c.sw, 0xdaabbccd, 4, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 9, 0, 2, c.lw, c.sw, 0xddaabbcc, 8, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 10, 1, 0, c.lw, c.sw, 0xcddaabbc, 12, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 11, 1, 1, c.lw, c.sw, 0xccddaabb, 16, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 12, 2, 0, c.lw, c.sw, 0xbccddaab, 20, tdat ); + + TEST_RV32C_ST_SRC21_BYPASS( 13, 0, 0, c.lw, c.sw, 0x00112233, 0, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 14, 0, 1, c.lw, c.sw, 0x30011223, 4, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 15, 0, 2, c.lw, c.sw, 0x33001122, 8, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 16, 1, 0, c.lw, c.sw, 0x23300112, 12, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 17, 1, 1, c.lw, c.sw, 0x22330011, 16, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 18, 2, 0, c.lw, c.sw, 0x12233001, 20, tdat ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0xdeadbeef +tdat2: .word 0xdeadbeef +tdat3: .word 0xdeadbeef +tdat4: .word 0xdeadbeef +tdat5: .word 0xdeadbeef +tdat6: .word 0xdeadbeef +tdat7: .word 0xdeadbeef +tdat8: .word 0xdeadbeef +tdat9: .word 0xdeadbeef +tdat10: .word 0xdeadbeef + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_swsp.S b/verification/self-tests/RV32C/c_swsp.S new file mode 100644 index 000000000..7b6250616 --- /dev/null +++ b/verification/self-tests/RV32C/c_swsp.S @@ -0,0 +1,75 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_swsp.S +#----------------------------------------------------------------------------- +# +# Test c_swsp instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_STSP_OP( 2, c.lwsp, c.swsp, 0x00aa00aa, 0, tdat ); + TEST_RV32C_STSP_OP( 3, c.lwsp, c.swsp, 0xaa00aa00, 4, tdat ); + TEST_RV32C_STSP_OP( 4, c.lwsp, c.swsp, 0x0aa00aa0, 8, tdat ); + TEST_RV32C_STSP_OP( 5, c.lwsp, c.swsp, 0xa00aa00a, 12, tdat ); + + # Test with a negative base + + TEST_CASE( 6, x13, 0x12345678, \ + la x11, tdat9; \ + li x12, 0x12345678; \ + addi x2, x11, -32; \ + c.swsp x12, 32(x2); \ + c.lwsp x13, 32(x2); \ + ) + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_STSP_SRC12_BYPASS( 7, 0, 0, c.lwsp, c.swsp, 0xaabbccdd, 0, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 8, 0, 1, c.lwsp, c.swsp, 0xdaabbccd, 4, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 9, 0, 2, c.lwsp, c.swsp, 0xddaabbcc, 8, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 10, 1, 0, c.lwsp, c.swsp, 0xcddaabbc, 12, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 11, 1, 1, c.lwsp, c.swsp, 0xccddaabb, 16, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 12, 2, 0, c.lwsp, c.swsp, 0xbccddaab, 20, tdat ); + + TEST_RV32C_STSP_SRC21_BYPASS( 13, 0, 0, c.lwsp, c.swsp, 0x00112233, 0, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 14, 0, 1, c.lwsp, c.swsp, 0x30011223, 4, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 15, 0, 2, c.lwsp, c.swsp, 0x33001122, 8, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 16, 1, 0, c.lwsp, c.swsp, 0x23300112, 12, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 17, 1, 1, c.lwsp, c.swsp, 0x22330011, 16, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 18, 2, 0, c.lwsp, c.swsp, 0x12233001, 20, tdat ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0xdeadbeef +tdat2: .word 0xdeadbeef +tdat3: .word 0xdeadbeef +tdat4: .word 0xdeadbeef +tdat5: .word 0xdeadbeef +tdat6: .word 0xdeadbeef +tdat7: .word 0xdeadbeef +tdat8: .word 0xdeadbeef +tdat9: .word 0xdeadbeef +tdat10: .word 0xdeadbeef + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_xor.S b/verification/self-tests/RV32C/c_xor.S new file mode 100644 index 000000000..7a6235ab5 --- /dev/null +++ b/verification/self-tests/RV32C/c_xor.S @@ -0,0 +1,62 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_xor.S +#----------------------------------------------------------------------------- +# +# Test c_xor instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 3, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_R_OP( 4, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 5, c.xor, 0x00ff00ff, 0xf00ff00f, 0xf0f0f0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 6, c.xor, 0x00000000, 0xff00ff00 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 7, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_DEST_BYPASS( 8, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_DEST_BYPASS( 9, 2, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC12_BYPASS( 10, 0, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 11, 0, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 12, 0, 2, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 13, 1, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 14, 1, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 15, 2, 0, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC21_BYPASS( 16, 0, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 17, 0, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 18, 0, 2, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 19, 1, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 20, 1, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 21, 2, 0, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END From f4fa7f3cbb1a7ea66d8a408d34c1025321647626 Mon Sep 17 00:00:00 2001 From: Jiahao Xu <54557771+jhsyu@users.noreply.github.com> Date: Sat, 27 Mar 2021 15:34:36 -0400 Subject: [PATCH 10/47] add RV32E support. (#9) * Initialize RV32E and DEV branch * update .gitignore * add RV32E support * Reverted changes to TESTNUM Co-authored-by: Jiahao Xu (socet94) Co-authored-by: Cole Nelson --- .gitignore | 8 +++ example.yml | 3 + scripts/config_core.py | 5 +- source_code/RV32E/rv32e_reg_file.sv | 66 +++++++++++++++++++ source_code/RV32E/rv32e_wrapper.sv | 14 ++++ .../pipelines/tspp/tspp_execute_stage.sv | 17 ++++- .../extensions/rv32m/pp_mul32_uvm/agent.svh | 28 -------- .../rv32m/pp_mul32_uvm/comparator.svh | 61 ----------------- .../rv32m/pp_mul32_uvm/component.svh | 22 ------- .../extensions/rv32m/pp_mul32_uvm/driver.svh | 50 -------------- .../extensions/rv32m/pp_mul32_uvm/env.svh | 36 ---------- .../extensions/rv32m/pp_mul32_uvm/monitor.svh | 41 ------------ .../extensions/rv32m/pp_mul32_uvm/mult_if.svh | 13 ---- .../rv32m/pp_mul32_uvm/predictor.svh | 31 --------- .../rv32m/pp_mul32_uvm/sequencer.svh | 35 ---------- .../extensions/rv32m/pp_mul32_uvm/tb_mult.sv | 25 ------- .../extensions/rv32m/pp_mul32_uvm/test.svh | 38 ----------- .../rv32m/pp_mul32_uvm/transaction.svh | 25 ------- source_code/standard_core/control_unit.sv | 1 - source_code/tb/tb_RISCVBusiness_self_test.sv | 4 +- source_code/wscript | 2 +- 21 files changed, 114 insertions(+), 411 deletions(-) create mode 100644 source_code/RV32E/rv32e_reg_file.sv create mode 100644 source_code/RV32E/rv32e_wrapper.sv delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/agent.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/comparator.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/component.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/driver.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/env.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/monitor.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/mult_if.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/predictor.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/sequencer.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/tb_mult.sv delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/test.svh delete mode 100644 source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/transaction.svh diff --git a/.gitignore b/.gitignore index 7bb588ef2..a3ccd9d56 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,12 @@ source_code/fpga/output_files mitll* # Ignore temporary files created by gedit *~ + +# ignore files auto-generated by vscode. +_info +_vmake +*.qdb +*.qtl +*.qpg + open_files.sh diff --git a/example.yml b/example.yml index 8dca312d4..8d153ea28 100644 --- a/example.yml +++ b/example.yml @@ -25,6 +25,9 @@ microarch_params: # Halt infinite_loop_halts : "true" + # base ISA + base_isa: "RV32I" + # RISC-MGMT Extension Configuration risc_mgmt_params: standard_extensions: diff --git a/scripts/config_core.py b/scripts/config_core.py index 132ba1944..892f5fb03 100755 --- a/scripts/config_core.py +++ b/scripts/config_core.py @@ -48,7 +48,10 @@ # Sparisty Optimizations 'sparce_enabled' : [ 'enabled', 'disabled' ], # RV32C - 'rv32c_enabled' : [ 'enabled', 'disabled' ] + 'rv32c_enabled' : [ 'enabled', 'disabled' ], + # base ISA Configurations + 'base_isa': ['RV32I', 'RV32E'], + # Halt Enable -- Good for testing, not for tapeout 'infinite_loop_halts' : ['true', 'false'] } diff --git a/source_code/RV32E/rv32e_reg_file.sv b/source_code/RV32E/rv32e_reg_file.sv new file mode 100644 index 000000000..e70799aef --- /dev/null +++ b/source_code/RV32E/rv32e_reg_file.sv @@ -0,0 +1,66 @@ +/* +* Copyright 2021 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32e_reg_file.sv +* +* Created by: Jiahao Xu +* Email: xu1392@purdue.edu +* Date Created: 2/26/2021 +* Description: Implenmentation of RV32E register file. +*/ +`include "rv32i_reg_file_if.vh" + +module rv32e_reg_file ( + input CLK, nRST, + rv32i_reg_file_if rf_if +); + import rv32i_types_pkg::*; + localparam NUM_REGS = 16; + + // input: + // rf_if.wen: write enable. + // rf_if.w_data [31:0]: the word to write. + // rf_if.rd [4:0]: the register that need to write. + // rf_if.rs1 [4:0]: the address of read reg 1. + // rf_if.rs2 [4:0]: the address of read reg 2. + // output + // rf_if.rs1_data [31:0]: the read data of rs1 + // rf_if.rs2_data [31:0]: the read data of rs2 + + + word_t [NUM_REGS-1 : 0] registers; + + always_ff @(posedge CLK, negedge nRST) begin : RV32E_REG_FILE_FF + if (~nRST) begin + registers <= '0; + end + // check if the rd is valid. + else if (rf_if.wen && (|rf_if.rd) && ~rf_if.rd[4]) begin + // if (WEN && rd!=0 && rd<16) write. + registers[rf_if.rd] <= rf_if.w_data; + end + else begin + // else keep the current register file unchanged. + registers <= registers; + end + end + // dispose the MSB of the select line since this register file + // has only 16 registers. + // the select line should be in [0,15] + // if invalid rs, return 0. + assign rf_if.rs1_data = rf_if.rs1[4] ? 0 : registers[rf_if.rs1]; + assign rf_if.rs2_data = rf_if.rs2[4] ? 0 : registers[rf_if.rs2]; +endmodule diff --git a/source_code/RV32E/rv32e_wrapper.sv b/source_code/RV32E/rv32e_wrapper.sv new file mode 100644 index 000000000..818963424 --- /dev/null +++ b/source_code/RV32E/rv32e_wrapper.sv @@ -0,0 +1,14 @@ +`include "component_selection_defines.vh" +`include "rv32i_reg_file_if.vh" +module rv32e_wrapper ( + input logic CLK, nRST, + rv32i_reg_file_if rf_if +); + generate + case (BASE_ISA) + "RV32I": rv32i_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); + "RV32E": rv32e_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); + default: rv32i_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); + endcase + endgenerate +endmodule \ No newline at end of file diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index 63ee5700a..901396d7a 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -68,7 +68,22 @@ module tspp_execute_stage( .rmgmt_req_reg_r(rm_if.req_reg_r), .rmgmt_req_reg_w(rm_if.req_reg_w) ); - rv32i_reg_file rf (.*); + // generate + // case (BASE_ISA) + // "RV32I": rv32i_reg_file rf (CLK, nRST, rf_if); + // "RV32E": rv32e_reg_file rf (CLK, nRST, rf_if); + // endcase + // endgenerate + + generate + if (BASE_ISA == "RV32E") begin: REG_FILE_SEL + rv32e_reg_file rf (CLK, nRST, rf_if); + end + else begin: REG_FILE_SEL + rv32i_reg_file rf (CLK, nRST, rf_if); + end + endgenerate + // rv32i_reg_file rf (.*); alu alu (.*); jump_calc jump_calc (.*); diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/agent.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/agent.svh deleted file mode 100644 index c3b04e208..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/agent.svh +++ /dev/null @@ -1,28 +0,0 @@ -import uvm_pkg::*; -`include "uvm_macros.svh" -`include "sequence.svh" -`include "driver.svh" -`include "monitor.svh" - -class agent extends uvm_agent; - `uvm_component_utils(agent) - sequencer sqr; - driver drv; - monitor mon; - - function new(string name, uvm_component parent = null); - super.new(name, parent); - endfunction - - virtual function void build_phase(uvm_phase phase); - sqr = sequencer::type_id::create("sqr", this); - drv = driver::type_id::create("drv", this); - mon = monitor::type_id::create("mon", this); - endfunction: build_phase - - - virtual function void connect_phase(uvm_phase phase); - drv.seq_item_port.connect(sqr.seq_item_export); - endfunction: connect_phase - -endclass: agent \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/comparator.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/comparator.svh deleted file mode 100644 index e8509bf2b..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/comparator.svh +++ /dev/null @@ -1,61 +0,0 @@ -import uvm_pkg::*; -`include "uvm_macros.svh" - -class comparator extends uvm_scoreboard; - `uvm_component_utils(comparator) - uvm_analysis_export #(transaction) expected_export; // receive result from predictor - uvm_analysis_export #(transaction) actual_export; // receive result from DUT - uvm_tlm_analysis_fifo #(transaction) expected_fifo; - uvm_tlm_analysis_fifo #(transaction) actual_fifo; - - int m_matches, m_mismatches; // records number of matches and mismatches - - function new( string name , uvm_component parent) ; - super.new( name , parent ); - m_matches = 0; - m_mismatches = 0; - endfunction - - function void build_phase( uvm_phase phase ); - expected_export = new("expected_export", this); - actual_export = new("actual_export", this); - expected_fifo = new("expected_fifo", this); - actual_fifo = new("actual_fifo", this); - endfunction - - function void connect_phase(uvm_phase phase); - expected_export.connect(expected_fifo.analysis_export); - actual_export.connect(actual_fifo.analysis_export); - endfunction - - task run_phase(uvm_phase phase); - transaction expected_tx; //transaction from predictor - transaction actual_tx; //transaction from DUT - forever begin - expected_fifo.get(expected_tx); - actual_fifo.get(actual_tx); - uvm_report_info("Comparator", $psprintf("\nexpected multiplier: %d - \nexpected multiplicand: %d - \nexpected product: %d - \n~~~~~~~~~~~~~~~~~\n - \nactual multiplier: %d - \nactual muliplicand: %d - \nactual product: %d\n", expected_tx.multiplier, expected_tx.multiplicand, expected_tx.product, - actual_tx.multiplier, actual_tx.multiplicand, actual.product)); - - if(expected_tx.compare(actual_tx)) begin - m_matches++; - uvm_report_info("Comparator", "Data Match"); - end else begin - m_mismatches++; - uvm_report_error("Comparator", "Error: Data Mismatch"); - end - end - endtask: connect_phase - - function void report_phase(uvm_phase phase); - uvm_report_info("Comparator", $sformatf("Matches: %0d", m_matches)); - uvm_report_info("Comparator", $sformatf("Mismatches: %0d", m_mismatches)); - endfunction: report_phase - -endclass \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/component.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/component.svh deleted file mode 100644 index 2cb245daa..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/component.svh +++ /dev/null @@ -1,22 +0,0 @@ - -class component extends uvm_component; - `uvm_component_utils(component) - - virtual dut_if pp_mult32_if; - - function new(string name, uvm_component parent = null); - super.new(name, parent); - endfunction - - virtual function void build_phase(uvm_phase phase); - endfunction: build_phase - - virtual function void connect_phase(uvm_phase phase); - endfunction: connect_phase - - task run_phase(uvm_phase phase); - endtask: run_phase - - function report_phase(uvm_phase phase); - endfunction: report phase -endclass \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/driver.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/driver.svh deleted file mode 100644 index 70bbb1d1a..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/driver.svh +++ /dev/null @@ -1,50 +0,0 @@ -import uvm_pkg::*; -`include "uvm_macros.svh" -`include "mult_if.svh" - -class driver extends uvm_driver#(transaction); - `uvm_component_utils(driver) - - function new(string name, uvm_component parent); - super.new(name, parent); - endfunction: new - - virtual mult_if vif; - - function void build_phase(uvm_phase phase); - super.build_phase(phase); - if( !uvm_config_db#(virtual mult_if)::get(this, "", "mult_vif", vif) ) begin - `uvm_fatal("Driver", "No interface"); - end - endfunction: build_phase - - task run_phase(uvm_phase phase); - transaction tx; - - forever begin - - @(posedge mult_if.CLK); - seq_item_port.get_next_item(tx); - DUT_reset(); - while(!vif.finished) { - vif.multiplicand = tx.multiplicand; - vif.multiplier = tx.multiplier; - vif.is_signed = 2'b0; - vif.start = 1'b1; - } - - @(posedge vif.clk); - seq_item_port.item_done(); - end - - endtask:run_phase - - - task DUT_reset(); - vif.nRST = 1; - @(posedge vif.CLK); - vif.nRST = 0; - @(posedge vif.CLK); - endtask - -endclass: driver \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/env.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/env.svh deleted file mode 100644 index 65511f6ec..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/env.svh +++ /dev/null @@ -1,36 +0,0 @@ -import uvm_pkg::*; -`include "uvm_macros.svh" -`include "agent.svh" -`include "mult_if.svh" -`include "comparator.svh" // uvm_scoreboard -`include "predictor.svh" // uvm_subscriber -`include "transaction.svh" // uvm_sequence_item - -class environment extends uvm_env; - `uvm_component_utils(environment) - - agent agt; // contains monitor and driver - predictor pred; // a reference model to check the result - comparator comp; // scoreboard - - function new(string name = "env", uvm_component parent = null); - super.new(name, parent); - endfunction - - - - function void build_phase(uvm_phase phase); - // instantiate all the components through factory method - agt = agent::type_id::create("agt", this); - pred = predictor::type_id::create("pred", this); - comp = comparator::type_id::create("comp", this); - - endfunction - - function void connect_phase(uvm_phase phase); - agt.mon.counter_ap.connect(pred.analysis_export); // connect monitor to predictor - pred.pred_ap.connect(comp.expected_export); // connect predictor to comparator - agt.mon.result_ap.connect(comp.actual_export); // connect monitor to comparator - endfunction - -endclass: environment \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/monitor.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/monitor.svh deleted file mode 100644 index deb3592ba..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/monitor.svh +++ /dev/null @@ -1,41 +0,0 @@ -import uvm_pkg::*; -`include "uvm_macros.svh" -`include "mult_if.svh" - -class monitor extends uvm_monitor; - `uvm_component_utils(monitor) - - virtual mult_if vif; - uvm_analysis_port #(transaction) mult_ap; - uvm_analysis_port #(transaction) result_ap; - transaction prev_tx; - - - virtual function void build_phase(uvm_phase phase); - if (!uvm_config_db#(virtual mult_if)::get(this, "", "mult_vif", vif)) begin - `uvm_fatal("monitor", "No virtual interface specified for this monitor instance") - end - endfunction - - virtual task run_phase(uvm_phase phase); - super.run_phase(phase); - prev_tx = transaction::type_id::create("prev_tx"); - forever begin - transaction tx; - @(posedge vif.clk); - tx = transaction::type_id::create("tx"); - tx.multiplicand = vif.multiplicand; - tx.multiplier = vif.multiplier; - if(!tx.input_equal(prev_tx) && tx.multiplicand != 'z && tx.multiplier != 'z) begin - mult_ap.write(tx); - while(!vif.finished) begin - @(posedge vif.clk) - end - tx.product = vif.product; - result_ap.write(tx); - prev_tx.copy(tx); - end - end - endtask: run_phase - -endclass \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/mult_if.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/mult_if.svh deleted file mode 100644 index efd78f2fd..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/mult_if.svh +++ /dev/null @@ -1,13 +0,0 @@ - -`ifndef MULT_IF_SVH -`define MULT_IF_SVH - - -interface mult_if(input logic clk) - logic nRST, start, finished; - logic [31:0] multiplicand, multiplier; - logic [1:0] is_signed; - logic [63:0] product; -endinterface: mult_if - -`endif \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/predictor.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/predictor.svh deleted file mode 100644 index cbb63af1e..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/predictor.svh +++ /dev/null @@ -1,31 +0,0 @@ -import uvm_pkg::*; -`include "uvm_macros.svh" -`include "transaction.svh" - -class predictor extends uvm_subscriber #(transaction); - `uvm_component_utils(predictor) - - uvm_analysis_port #(transaction) pred_ap; - transaction output_tx; - - function new(string name, uvm_component parent = null); - super.new(name, parent); - endfunction: new - - function void build_phase(uvm_phase phase); - pred_ap = new("pred_ap", this); - endfunction - - function void write(transaction tx); - output_tx = transaction::type_id::create("output_tx", this); - output_tx.copy(tx); - output_tx.product = 64'b0; - - for(int i = 0; i < tx.multiplier; i++) { - output_tx.product += tx.multiplicand; - } - - pred_ap.write(output_tx); - - endfunction: write -endclass \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/sequencer.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/sequencer.svh deleted file mode 100644 index ca29e21ca..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/sequencer.svh +++ /dev/null @@ -1,35 +0,0 @@ - -import uvm_pkg::*; -`include "uvm_macros.svh" - -`include "transaction.svh" - -typedef uvm_sequencer #(transaction) sequencer; - - - -class mult_sequence extends uvm_sequence #(transaction); - `uvm_object_utils(counter_sequence) - function new(string name = ""); - super.new(name); - endfunction: new - - task body(); - forever - begin - transaction tx; - tx = transaction::type_id::create("tx"); - start_item(tx); - assert(tx.randomize()); - finish_item(tx); - end - endtask:body -endclass:counter_sequence - - -class sequencer extends uvm_sequence #(transaction); - `uvm_object_utils(mult_sequence) - function new(string name = "sequencer", uvm_component parent=null); - super.new(name); - endfunction: new -endclass: mult_sequencer diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/tb_mult.sv b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/tb_mult.sv deleted file mode 100644 index dfa1f4bb1..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/tb_mult.sv +++ /dev/null @@ -1,25 +0,0 @@ -`include "pp_mul32.sv" -`include "mult_if.svh" -`include "test.svh" - -`timescale 1ps/1ps -import uvm_pkg::*; - -module tb_mult(); - logic clk; - - initial begin - clk = 0; - forever #10 clk = !clk; - end - - mult_if pp_mult32_if(clk); - pp_mult32 MULT(pp_mult32_if); - - initial - begin - uvm_config_db#(virtual mult_if)::set( null, "", "vif", fc_if); - run_test("test"); - end - -endmodule: tb_mult; \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/test.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/test.svh deleted file mode 100644 index d1d843faa..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/test.svh +++ /dev/null @@ -1,38 +0,0 @@ - -import uvm_pkg::*; -`include "uvm_macros.svh" -`include "environment.svh" - -class test extends uvm_test; - `uvm_component_utils(test) - - environment env; - virtual mult_if vif; - counter_sequence seq; - - function new(string name = "test", uvm_component parent); - super.new(name, parent); - endfunction: new - - function void build_phase(uvm_phase phase); - super.build_phase(phase); - env = environment::type_id::create("env",this); - seq = mult_sequence::type_id::create("seq"); - - if (!uvm_config_db#(virtual mult_if)::get(this, "", "vif", vif)) begin - `uvm_fatal("TEST", "No virtual interface specified for this test instance") - end - uvm_config_db#(virtual counter_if)::set(this, "env.agt*", "vif", vif); - - - endfunction: build_phase - - task run_phase(uvm_phase phase); - phase.raise_objection( this, "Starting sequence in main phase" ); - $display("%t Starting sequence run_phase",$time); - seq.start(env.agt.sqr); - #100ns; - phase.drop_objection( this , "Finished in main phase" ); - endtask - -endclass: test \ No newline at end of file diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/transaction.svh b/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/transaction.svh deleted file mode 100644 index 0fc0c70be..000000000 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32_uvm/transaction.svh +++ /dev/null @@ -1,25 +0,0 @@ -`ifndef TRANSACTION_SVH -`define TRANSACTION_SVH - -import uvm_pkg::*; -`include "uvm_macros.svh" - -class transaction extends uvm_sequence_item; - `uvm_object_utils(transaction) - rand bit [31:0] multiplicand; - rand bit [31:0] multiplier; - rand bit [1:0] is_signed; - bit finished; - bit [63:0] product; - - constraint c_multiplicand {multiplicand > 0; multiplicand < 31'b100000}; - constraint c_multiplier {multiplier > 0; multiplier < 31'b100000}; - - - function new(string name = "transaction"); - super.new(name); - endfunction: new - -endclass: transaction - -`endif \ No newline at end of file diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index fe025b04a..09fc7ab2f 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -254,7 +254,6 @@ module control_unit end end end - assign cu_if.csr_rw_valid = (cu_if.csr_swap | cu_if.csr_set | cu_if.csr_clr); assign cu_if.csr_addr = csr_addr_t'(instr_i.imm11_00); diff --git a/source_code/tb/tb_RISCVBusiness_self_test.sv b/source_code/tb/tb_RISCVBusiness_self_test.sv index 5311b6524..e10d6a5c8 100644 --- a/source_code/tb/tb_RISCVBusiness_self_test.sv +++ b/source_code/tb/tb_RISCVBusiness_self_test.sv @@ -167,9 +167,9 @@ module tb_RISCVBusiness_self_test (); // Check Register 28 to see if test passed or failed if (clk_count == `RVBSELF_CLK_TIMEOUT) $display("ERROR: Test timed out"); - else if(DUT.execute_stage_i.rf.registers[28] != 32'h1) + else if(DUT.execute_stage_i.REG_FILE_SEL.rf.registers[28] != 32'h1) $display("ERROR: Test %0d did not pass", - (DUT.execute_stage_i.rf.registers[28] - 1)/2); + (DUT.execute_stage_i.REG_FILE_SEL.rf.registers[28] - 1)/2); else $display("SUCCESS"); $finish; diff --git a/source_code/wscript b/source_code/wscript index 6d9dd6cc5..4b1845f2a 100644 --- a/source_code/wscript +++ b/source_code/wscript @@ -1,5 +1,5 @@ def configure(ctx): - rvb_srcs = ['caches', 'branch_predictors', 'bus_bridges', 'trackers', 'risc_mgmt', 'privs', 'pipelines', 'standard_core', 'ram', 'sparce', 'rv32c'] + rvb_srcs = ['caches', 'branch_predictors', 'bus_bridges', 'trackers', 'risc_mgmt', 'privs', 'pipelines', 'standard_core', 'ram', 'sparce', 'rv32c', 'RV32E'] ctx.SFFUnits.add('RISCVBusiness_packages', src_dir = ['packages'] ) From 1e9e72aec60e2c7ad85b09fab36a0fab0587c9eb Mon Sep 17 00:00:00 2001 From: raghu1996 Date: Thu, 1 Apr 2021 13:58:19 -0400 Subject: [PATCH 11/47] WFI detection for clock manager (#10) * WFI detection for clock_manager Co-authored-by: Project47 Co-authored-by: Cole Nelson --- source_code/include/control_unit_if.vh | 4 ++-- source_code/packages/rv32i_types_pkg.sv | 3 ++- source_code/pipelines/tspp/tspp_execute_stage.sv | 5 +++-- source_code/standard_core/RISCVBusiness.sv | 4 +++- source_code/standard_core/control_unit.sv | 4 +++- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/source_code/include/control_unit_if.vh b/source_code/include/control_unit_if.vh index cb2e32772..29049f6e6 100644 --- a/source_code/include/control_unit_if.vh +++ b/source_code/include/control_unit_if.vh @@ -31,7 +31,7 @@ interface control_unit_if; import rv32i_types_pkg::*; import machine_mode_types_1_11_pkg::*; - logic dwen, dren, j_sel, branch, jump, ex_pc_sel, imm_shamt_sel, halt, wen, ifence; + logic dwen, dren, j_sel, branch, jump, ex_pc_sel, imm_shamt_sel, halt, wen, ifence, wfi; aluop_t alu_op; logic [1:0] alu_a_sel, alu_b_sel; logic [2:0] w_sel; @@ -57,7 +57,7 @@ interface control_unit_if; imm_I, imm_S, imm_SB, imm_UJ, imm_U, imm_shamt_sel, alu_op, opcode, halt, wen, fault_insn, illegal_insn, ret_insn, breakpoint, ecall_insn, csr_swap, csr_set, csr_clr, csr_imm, csr_rw_valid, - csr_addr, zimm, ifence + csr_addr, zimm, ifence, wfi ); endinterface diff --git a/source_code/packages/rv32i_types_pkg.sv b/source_code/packages/rv32i_types_pkg.sv index 43968cc7e..92d79f295 100644 --- a/source_code/packages/rv32i_types_pkg.sv +++ b/source_code/packages/rv32i_types_pkg.sv @@ -126,7 +126,8 @@ package rv32i_types_pkg; EBREAK = 12'b0000000_00001, MRET = 12'b0011000_00010, SRET = 12'b0001000_00010, - URET = 12'b0000000_00010 + URET = 12'b0000000_00010, + WFI = 12'b0001000_00101 } priv_insn_t; typedef enum logic [2:0] { diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index 901396d7a..84d777688 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -46,7 +46,8 @@ module tspp_execute_stage( risc_mgmt_if.ts_execute rm_if, cache_control_if.pipeline cc_if, sparce_pipeline_if.pipe_execute sparce_if, - rv32c_if.execute rv32cif + rv32c_if.execute rv32cif, + output logic wfi ); import rv32i_types_pkg::*; @@ -74,7 +75,7 @@ module tspp_execute_stage( // "RV32E": rv32e_reg_file rf (CLK, nRST, rf_if); // endcase // endgenerate - + assign wfi = cu_if.wfi; //Added by rkannank generate if (BASE_ISA == "RV32E") begin: REG_FILE_SEL rv32e_reg_file rf (CLK, nRST, rf_if); diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index a76582302..f5cadb96e 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -35,6 +35,7 @@ module RISCVBusiness ( input logic CLK, nRST, + output logic wfi, core_interrupt_if.core interrupt_if, `ifdef BUS_INTERFACE_GENERIC_BUS generic_bus_if.cpu gen_bus_if @@ -102,7 +103,8 @@ module RISCVBusiness ( .rm_if(rm_if), .cc_if(cc_if), .sparce_if(sparce_if), - .rv32cif(rv32cif) + .rv32cif(rv32cif), + .wfi(wfi) ); tspp_hazard_unit hazard_unit_i ( diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index 09fc7ab2f..b482c5aed 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -85,7 +85,6 @@ module control_unit assign cu_if.jump = (cu_if.opcode == JAL || cu_if.opcode == JALR); assign cu_if.ex_pc_sel = (cu_if.opcode == JAL || cu_if.opcode == JALR); assign cu_if.j_sel = (cu_if.opcode == JAL); - // Assign alu operands always_comb begin case(cu_if.opcode) @@ -214,6 +213,7 @@ module control_unit cu_if.ret_insn = 1'b0; cu_if.breakpoint = 1'b0; cu_if.ecall_insn = 1'b0; + cu_if.wfi = 1'b0; if (cu_if.opcode == SYSTEM) begin if (rv32i_system_t'(instr_i.funct3) == PRIV) begin @@ -223,6 +223,8 @@ module control_unit cu_if.breakpoint = 1'b1; if (priv_insn_t'(instr_i.imm11_00) == ECALL) cu_if.ecall_insn = 1'b1; + if (priv_insn_t'(instr_i.imm11_00) == WFI) + cu_if.wfi = 1'b1; end end end From 5fb659b9562b9c238fc5676e2820689772c7bf78 Mon Sep 17 00:00:00 2001 From: Cole Nelson <35197931+cole-nelson@users.noreply.github.com> Date: Wed, 14 Sep 2022 20:15:58 -0400 Subject: [PATCH 12/47] Verilator (#19) Initial support for Verilator in RISCVBusiness. Adds Makefile supporting Verilator along wtih small fixes to ensure compilation. Adds an updated "run_tests_verilator" based on @ngildenhuys improved run_tests script. Co-authored-by: Mitch Co-authored-by: Hadi Ahmed --- .github/workflows/test.yml | 19 + .gitignore | 10 +- Makefile | 57 +++ dev.yml | 35 ++ run_tests_config.json | 15 + run_tests_verilator.py | 447 ++++++++++++++++++ scripts/config_core.py | 4 +- scripts/config_core_2.py | 230 +++++++++ source_code/caches/separate_caches.sv | 4 + source_code/include/core_interrupt_if.vh | 32 +- .../pipelines/tspp/tspp_execute_stage.sv | 2 +- .../privs/priv_1_11/priv_1_11_csr_rfile.sv | 113 ++--- .../priv_1_11/priv_1_11_pipeline_control.sv | 2 +- source_code/rv32c/fetch_buffer.sv | 158 +++---- source_code/rv32c/rv32c_wrapper.sv | 2 + source_code/standard_core/RISCVBusiness.sv | 10 +- source_code/standard_core/top_core.sv | 83 ++++ tb_core.cc | 191 ++++++++ updated_run_tests.py | 435 +++++++++++++++++ 19 files changed, 1691 insertions(+), 158 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 Makefile create mode 100644 dev.yml create mode 100644 run_tests_config.json create mode 100644 run_tests_verilator.py create mode 100755 scripts/config_core_2.py create mode 100644 source_code/standard_core/top_core.sv create mode 100644 tb_core.cc create mode 100644 updated_run_tests.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..c162cf9ba --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,19 @@ +name: Test +on: + push: + pull_request: + branches: [main] + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v3 # required + - name: Run Regression Tests + uses: Purdue-SoCET/SoCET-CI@main # core usage + with: + targets: | # List all FuseSoC Test Targets + make config + make verilate \ No newline at end of file diff --git a/.gitignore b/.gitignore index a3ccd9d56..ea3f5ec84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # ignore the build and run files build/ sim_out/ +obj_dir/ # ignore memory files from test cases *.hex meminit.ver @@ -30,8 +31,15 @@ source_code/fpga/output_files mitll* # Ignore temporary files created by gedit *~ +# Ignore test cache files +run_tests_cache.json +# Ignore log files and Verilator outputs +*.log +*.fst +memsim.dump +meminit.bin -# ignore files auto-generated by vscode. +# ignore files auto-generated by vscode. _info _vmake *.qdb diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..7dd22f857 --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ +ROOT := $(shell pwd) + +RISCV := $(ROOT)/source_code +RISCV_CORE := $(RISCV)/standard_core +PIPELINE := $(RISCV)/pipelines +RISCV_PKGS := $(RISCV)/packages +RISC_MGMT := $(RISCV)/risc_mgmt +SPARCE := $(RISCV)/sparce +PRIVS := $(RISCV)/privs +BRANCH_PREDICT := $(RISCV)/branch_predictors +CACHES := $(RISCV)/caches +RISCV_BUS := $(RISCV)/bus_bridges +RV32C := $(RISCV)/rv32c +RV32M_FILES := $(RISC_MGMT)/extensions/rv32m/carry_save_adder.sv $(RISC_MGMT)/extensions/rv32m/flex_counter_mul.sv $(RISC_MGMT)/extensions/rv32m/full_adder.sv $(RISC_MGMT)/extensions/rv32m/pp_mul32.sv $(RISC_MGMT)/extensions/rv32m/radix4_divider.sv $(RISC_MGMT)/extensions/rv32m/rv32m_decode.sv $(RISC_MGMT)/extensions/rv32m/rv32m_execute.sv $(RISC_MGMT)/extensions/rv32m/rv32m_memory.sv +RV32C_FILES := $(RV32C)/decompressor.sv $(RV32C)/fetch_buffer.sv $(RV32C)/rv32c_disabled.sv $(RV32C)/rv32c_enabled.sv $(RV32C)/rv32c_wrapper.sv +RISC_MGMT_FILES := $(RISC_MGMT)/risc_mgmt_wrapper.sv $(RISC_MGMT)/tspp/tspp_risc_mgmt.sv $(RV32M_FILES) +RISC_EXT_FILES := $(RISC_MGMT)/extensions/template/template_decode.sv $(RISC_MGMT)/extensions/template/template_execute.sv $(RISC_MGMT)/extensions/template/template_memory.sv +CORE_PKG_FILES := $(RISCV_PKGS)/rv32i_types_pkg.sv $(RISCV_PKGS)/alu_types_pkg.sv $(RISCV_PKGS)/risc_mgmt/template_pkg.sv $(RISCV_PKGS)/risc_mgmt/crc32_pkg.sv $(RISCV_PKGS)/risc_mgmt/rv32m_pkg.sv $(RISCV_PKGS)/risc_mgmt/test_pkg.sv $(RISCV_PKGS)/machine_mode_types_1_11_pkg.sv $(RISCV_PKGS)/machine_mode_types_pkg.sv +CORE_FILES := $(RISCV_CORE)/alu.sv $(RISCV_CORE)/branch_res.sv $(RISCV_CORE)/control_unit.sv $(RISCV_CORE)/dmem_extender.sv $(RISCV_CORE)/endian_swapper.sv $(RISCV_CORE)/jump_calc.sv $(RISCV_CORE)/memory_controller.sv $(RISCV_CORE)/RISCVBusiness.sv $(RISCV_CORE)/rv32i_reg_file.sv $(RISCV_CORE)/top_core.sv +PIPELINE_FILES := $(PIPELINE)/tspp/tspp_execute_stage.sv $(PIPELINE)/tspp/tspp_fetch_stage.sv $(PIPELINE)/tspp/tspp_hazard_unit.sv #$(PIPELINE)/tspp/tspp.sv +PREDICTOR_FILES := $(BRANCH_PREDICT)/branch_predictor_wrapper.sv $(BRANCH_PREDICT)/nottaken_predictor/nottaken_predictor.sv +PRIV_FILES := $(PRIVS)/priv_wrapper.sv $(PRIVS)/priv_1_11/priv_1_11_block.sv $(PRIVS)/priv_1_11/priv_1_11_control.sv $(PRIVS)/priv_1_11/priv_1_11_csr_rfile.sv $(PRIVS)/priv_1_11/priv_1_11_pipeline_control.sv +CACHE_FILES := $(CACHES)/caches_wrapper.sv $(CACHES)/pass_through/pass_through_cache.sv $(CACHES)/direct_mapped_tpf/direct_mapped_tpf_cache.sv $(CACHES)/separate_caches.sv +SPARCE_FILES := $(SPARCE)/sparce_wrapper.sv $(SPARCE)/sparce_disabled/sparce_disabled.sv $(SPARCE)/sparce_enabled/sparce_cfid.sv $(SPARCE)/sparce_enabled/sparce_enabled.sv $(SPARCE)/sparce_enabled/sparce_psru.sv $(SPARCE)/sparce_enabled/sparce_sasa_table.sv $(SPARCE)/sparce_enabled/sparce_sprf.sv $(SPARCE)/sparce_enabled/sparce_svc.sv +RISCV_BUS_FILES := $(RISCV_BUS)/generic_nonpipeline.sv #$(RISCV_BUS)/ahb.sv +TRACKER_FILES := $(RISCV)/trackers/cpu_tracker.sv $(RISCV)/trackers/branch_tracker.sv + +COMPONENT_FILES_SV := $(CORE_PKG_FILES) $(RISC_MGMT_FILES) $(RISC_EXT_FILES) $(CORE_FILES) $(RV32C_FILES) $(PIPELINE_FILES) $(SPARCE_FILES) $(PREDICTOR_FILES) $(PRIV_FILES) $(CACHE_FILES) $(RISCV_BUS_FILES) $(TRACKER_FILES) + +TOP_ENTITY := RISCVBusiness + +HEADER_FILES := -I$(RISCV)/include + + +define USAGE +@echo "-----------------------------------------" +@echo " Build Targets:" +@echo " config: config core with example.yml" +@echo " verilate: Invoke verilator" +@echo "-----------------------------------------" +endef + +.phony: default clean + + +default: + $(USAGE) + +config: + python3 scripts/config_core.py example.yml + +verilate: + verilator -Wno-UNOPTFLAT -Wno-SYMRSVDWORD -cc -Wno-lint --report-unoptflat --trace-fst --trace-structs --top-module top_core $(HEADER_FILES) $(COMPONENT_FILES_SV) --exe tb_core.cc + $(MAKE) -C obj_dir -f Vtop_core.mk -j `nproc` + +clean: + rm -rf obj_dir diff --git a/dev.yml b/dev.yml new file mode 100644 index 000000000..02895a91a --- /dev/null +++ b/dev.yml @@ -0,0 +1,35 @@ +# ISA Configurations +isa_params: + xlen : 32 + +# Microarchitectural Configurations +microarch_params: + # Branch/Jump Configurations + br_predictor_type : "not_taken" + + # Cache configurations + cache_config : "separate" + dcache_type : "pass_through" + icache_type : "pass_through" + + # Bus configurations + bus_endianness : "big" + bus_interface_type : "ahb_if" + + # Sparisty Optimizations + sparce_enabled : "disabled" + + # RV32C + rv32c_enabled : "enabled" + + # Halt + infinite_loop_halts : "true" + + # base ISA + base_isa: "RV32I" + +# RISC-MGMT Extension Configuration +risc_mgmt_params: + standard_extensions: + nonstandard_extensions: + diff --git a/run_tests_config.json b/run_tests_config.json new file mode 100644 index 000000000..93a909fd8 --- /dev/null +++ b/run_tests_config.json @@ -0,0 +1,15 @@ +{ + "arch": "RV32I", + "abi": "ilp32", + "xlen": "rv32imc", + "test_type": "self-tests", + "top_level": "RISCVBusiness_self_test", + "test_filenames": ["*.S"], + + "cache_file": "run_tests_cache.json", + "build_dir": "./build", + "verif_dir": "./verification", + "asm_env": "./verification/asm-env/selfasm", + "sim_dir": "./sim_out", + "link_file": "link.ld" +} diff --git a/run_tests_verilator.py b/run_tests_verilator.py new file mode 100644 index 000000000..c007417d6 --- /dev/null +++ b/run_tests_verilator.py @@ -0,0 +1,447 @@ +#!/usr/bin/python3 + +# +# Copyright 2022 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: updated_run_tests.py +# +# Created by: Nicholas Gildenhuys +# Email: ngildenh@purdue.edu +# Date Created: 03/29/2022 +# Description: A better script for running processor tests + + +######## Library Imports ######## +import argparse +from asyncio.log import logger +import sys +import os +import re +from typing import List, Type +import logging +import subprocess +import pathlib +import glob +import json +import itertools + + +######## Globals ######## +END_COLOR = "\033[0m" +GREEN = "\033[92m" +RED = "\033[31m" +#WAF_LOGFILE = "waf_output.log" +#BUILD_LOGFILE = "build_log.log" +DEFAULT_CONFIG_FILE = "run_tests_config.json" +MEMINIT_HEX_FILE = "meminit.hex" +ELF2HEX_COMAND = "/home/ecegrid/a/socpub/Public/riscv_dev/riscv_installs/RV_current/bin/elf2hex" +FAILED = "Failed" +PASSED = "Passed" + +######## Error Code Exception Class ######## +class Error(Exception): + """A custom exception class for raising exceptions from sub processes""" + def __init__(self, error_string: str): + self.error_string = error_string + def __str__(self) -> str: + return self.error_string + +class run_config(): + def __init__(self, config_json:dict): + # top level info + self.arch = str(config_json["arch"]) + self.abi = str(config_json["abi"]) + self.xlen = str(config_json["xlen"]) + self.test_type = str(config_json["test_type"]) + self.top_level = str(config_json["top_level"]) + # read directory definitions + self.build_dir = pathlib.Path(config_json["build_dir"]) + self.verif_dir = pathlib.Path(config_json["verif_dir"]) + self.asm_env = pathlib.Path(config_json["asm_env"]) + # directory of all where the test files are + self.test_dir = self.verif_dir/self.test_type/self.arch + + # output directories + self.sim_dir = pathlib.Path(config_json["sim_dir"]) + self.out_dir = self.sim_dir/self.arch + + # file definitions + self.link_file = self.verif_dir/pathlib.Path("asm-env")/config_json["link_file"] + # the cache file name, expects it in same as run directory + self.cache_file = pathlib.Path(config_json["cache_file"]) + # location of the test files + if(not self.test_dir.exists()): + raise Error(f"Test Directory: {self.test_dir} does not exists") + # init for apending later + self.test_filepaths = [] + self.test_filenames = config_json["test_filenames"] + return + +######## Bulk Work Functions ######## +def run_tests(config: Type[run_config]) -> List[str]: + """ + Should return the list of tests that were failed + """ + print(f"Running Strings: {config.test_filepaths}") + # setup the sim out directory + if(not config.out_dir.exists()): + os.makedirs(config.out_dir) + + # setup the loggers + # the log names don't matter as they will be replaced in the for loop + build_logger = setup_logger("build_logger", pathlib.Path("./build_log.log")) + waf_logger = setup_logger("waf_logger", pathlib.Path("./waf_log.log")) + + # setup caching dict mechanism + test_status = dict() + # read in the cached results if possible + if(pathlib.Path(config.cache_file).exists()): + with open(config.cache_file, "r") as cache_fp: + test_status = json.load(cache_fp) + + # run the tests + try: + for file in config.test_filepaths: + filepath = pathlib.Path(file) + # check if there is a cached result + test_cached_result = FAILED + if(filepath.stem in test_status): + test_cached_result = test_status[filepath.stem] + + # skip files that begin with _ + # or files that have already passed + if(filepath.name[0] == '_' or test_cached_result == PASSED): + print(f"Skipping Test: {filepath.name}") + continue + print(f"-----------------------------") + print(f"Running Test: {filepath.name}") + # setup the test output directory if it is not there + test_out_dir = config.out_dir/filepath.stem + # setup the sim out folder + if(not test_out_dir.exists()): + os.makedirs(test_out_dir) + # update the loggers + build_log_filepath = test_out_dir/str(filepath.stem+"_build.log") + build_logger = change_logger_file_handlers(build_logger, build_log_filepath) + waf_log_filepath = test_out_dir/str(filepath.stem+"_waf.log") + waf_logger = change_logger_file_handlers(waf_logger, waf_log_filepath) + + # compile the assembly file + print(f" - Compiling...") + outpath = test_out_dir/str(filepath.stem+".elf") + hex_filepath = compile_asm(filepath, outpath, config, build_logger) + # clean up the hex file - needs writting + #clean_init_hex(hex_filepath) + print(f" - Running Verilator...") + # run self sim - basic done + res = run_sim(config.top_level, waf_logger) + #waf_log_filepath = pathlib.Path(waf_logger.handlers[0].baseFilename) + print(f" - Checking Results...") + if 'PASSED' in res: + print(GREEN + '[PASSED]' + END_COLOR) + result = PASSED + else: + print(RED + '[FAILED]' + END_COLOR) + result = FAILED + # check results - + #result = check_self_results(waf_log_filepath, build_logger) + + # cache the result in the dict + test_status[filepath.stem] = result + #print(test_status) + # catch keyboard interrupts to flush the cache file + except KeyboardInterrupt: + pass # yes I know that this is not the pest practice + + with open(config.cache_file, "w") as cache_fp: + json.dump(test_status, cache_fp) + + return + + +# compile the assembly file - done +def compile_asm(filepath: Type[pathlib.Path], outpath: Type[pathlib.Path],\ + config: Type[run_config], logger: Type[logging.Logger])\ + -> Type[pathlib.Path]: + # main compile arguments + # notes: need to parameratize the .T, .I, abi, and xlen flags + # also probably pass the filepath too + compile_cmd_arr = ["riscv64-unknown-elf-gcc", + "-march=" + config.xlen, "-mabi=" + config.abi, + "-static", "-mcmodel=medany", "-fvisibility=hidden", + "-nostdlib", "-nostartfiles", + "-T"+str(config.link_file), + "-I"+str(config.asm_env), str(filepath), "-o", + str(outpath)] + + log_header("riscv64-unknown-elf-gcc", logger) + try: + compile_process: Type[subprocess.CompletedProcess] = subprocess.run(compile_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except subprocess.CalledProcessError as p_error: + compile_process = p_error + finally: + log_subprocess(compile_process, logger) + + + # create the mem init file + # NOTE: Hard coded values here that I am too lazy to config + #elf_2_hex_cmd_arr = [ELF2HEX_COMAND, "8", "65536", str(outpath), "2147483648"] + elf_2_bin = ['riscv64-unknown-elf-objcopy', '-O', 'binary', str(outpath), 'meminit.bin'] + # hex file return this for cleaning + hex_filepath = outpath.parent/MEMINIT_HEX_FILE + + log_header("elf2hex", logger) + elf_2_hex_process: Type[subprocess.CompletedProcess] = subprocess.run(elf_2_bin, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + log_subprocess(elf_2_hex_process, logger, level=logging.ERROR) + #with open(hex_filepath, "w") as hex_file: + # hex_file.write(str(elf_2_hex_process.stdout, encoding="utf-8")) + + logger.info(f"Finished {filepath.name} Compilation") + + return hex_filepath + +# run the simulation +def run_sim(top_level: str, logger: Type[logging.Logger]) -> None: + + # TODO: Assuming simulator already built + cmd_arr = ['./obj_dir/Vtop_core', 'meminit.bin'] + res = subprocess.run(cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + + #config_cmd_arr = ["waf", "configure", "--top_level=" + top_level] + #log_header("waf configure", logger) + #config_process = subprocess.run(config_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + #log_subprocess(config_process, logger) + + #sim_cmd_arr = ["waf", "verify_source"] + #log_header("waf verify_source", logger) + #sim_process = subprocess.run(sim_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + #log_subprocess(sim_process, logger) + + return res.stdout + +def run_spike_asm(filepath: str) -> None: + return +# compare the results +def compare_results(filepath: str) -> bool: + return False +# compare spike results +def compare_spike_results(): + return +# just check selfasm results +def check_self_results(filepath: Type[pathlib.Path],\ + logger: Type[logging.Logger]) -> str: + # filepath should be path to the waf_output.log + short_name = filepath.parent.stem + pass_msg = '{0:<10}{1}'.format(short_name,GREEN + '[PASSED]' + END_COLOR) + fail_msg = '{0:<10}{1}'.format(short_name,RED + '[FAILED]' + END_COLOR) + with open(filepath, 'r') as waf_output: + waf_output_text = waf_output.read() + match = re.search(r'SUCCESS', waf_output_text) + if match: + print(pass_msg) + logger.info(pass_msg) + return PASSED + else: + match = re.search(r'(ERROR:)\s+(.*)', waf_output_text) + print(match.group(2)) + print(fail_msg) + logger.error(fail_msg) + return FAILED + +######## Side Helper Function ######## +def log_subprocess(complete_proc: Type[subprocess.CompletedProcess],\ + logger: Type[logging.Logger], level=logging.NOTSET) -> None: + """ Logs the std output to the logfile, then if there is an error + it raises an exception that prints out the output from stderr + """ + # this is mainly for the elf2hex so we dont get the hexdump in the log + # but we can still get errors + if(level <= logging.INFO): + logger.info(str(complete_proc.stdout, encoding="utf-8")) + # put in error if there are any and raise an exception + if(complete_proc.returncode): + error_string = str(complete_proc.stderr, encoding="utf-8") + logger.error(error_string) + raise Error(error_string=error_string) + return +def log_header(process: str, logger: Type[logging.Logger]) -> None: + logger.info(f"============ Starting {process} Process ============") + return + +# Create a temp file that consists of the Intel HEX format +# version of the meminit.hex file, delete the original log file +# and rename the temp file to the original's name +def clean_init_hex(filepath: Type[pathlib.Path]) -> None: + # filepath is the path object to the dirty meminit.hex file + # the one that was generated by the elf to hex + #short_name = file_name.split(ARCH+'/')[1][:-2] + #short_name = filepath.stem + #output_dir = './sim_out/' + ARCH + '/' + short_name + '/' + #output_dir = filepath.parent + #init_output = output_dir + 'meminit.hex' + init_output = filepath + build_dir = pathlib.Path("./build/") + + #cleaned_location = init_output[:len(file_name)-4] + "_clean.hex" + cleaned_location = filepath.parent/"meminit_clean.hex" + addr = 0x00 + with open(init_output, 'r') as init_file: + with open(cleaned_location, 'w')as cleaned_file: + for line in init_file: + stripped_line = line[:len(line)-1] + for i in range(len(stripped_line), 0, -8): + data_word = stripped_line[i-8:i] + new_data_word = data_word[6:8] + data_word[4:6] + new_data_word += data_word[2:4] + data_word[0:2] + checksum = calculate_checksum_str(int(new_data_word, 16), addr) + if len(checksum) < 2: + checksum = '0' + checksum + addr_str = hex(addr//4)[2:] + #left pad the string with 0s until 4 hex digits + while len(addr_str) < 4: + addr_str = '0' + addr_str + if new_data_word != "00000000": + out = ":04" + addr_str + "00" + new_data_word + checksum + '\n' + cleaned_file.write(out) + addr += 0x4 + # add the EOL record to the file + cleaned_file.write(":00000001FF") + + + subprocess.call(['mv', str(init_output), str(init_output.parent/"meminit_dirty.hex")]) + subprocess.call(['mv', str(cleaned_location), str(init_output)]) + if not os.path.exists(build_dir): + os.makedirs(build_dir) + subprocess.call(['cp', str(init_output), str(build_dir/"meminit.hex")]) + return + +# Returns the string representation of the +# checksum for the given data and address values +def calculate_checksum_str(data: int, addr: int) -> str: + addr = addr//4 + high_addr = (addr & 0xFF00) >> 8 + low_addr = addr & 0x00FF + data1 = data & 0x000000FF + data2 = (data & 0x0000FF00) >> 8 + data3 = (data & 0x00FF0000) >> 16 + data4 = (data & 0xFF000000) >> 24 + checksum = 4 + high_addr + low_addr + checksum += data1 + data2 + data3 + data4 + checksum = checksum & 0xFF + checksum = int(invert_bin_string(bin(checksum)[2:]),2) + checksum += 1 + checksum_lower_byte = hex(checksum)[2:] + if len(checksum_lower_byte) > 2: + checksum_lower_byte = checksum_lower_byte[-2:] + return checksum_lower_byte + +def invert_bin_string(bin_string: str) -> str: + inverted = '' + while len(bin_string) < 8: + bin_string = '0' + bin_string + for bit in bin_string: + if bit == '0': + inverted = inverted + '1' + else: + inverted = inverted + '0' + return inverted + +######## Logger Related Functions ######## +def setup_logger(name: str, log_filepath: Type[pathlib.Path],\ + level=logging.DEBUG) -> Type[logging.Logger]: + handler = logging.FileHandler(filename=log_filepath, mode="w") + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + logger = logging.getLogger(name) + logger.setLevel(level) + logger.addHandler(handler) + return logger + +def change_logger_file_handlers(logger: Type[logging.Logger],\ + new_filepath: Type[pathlib.Path]) -> Type[logging.Logger]: + """ Removes all old file handlers and replaces it with a new one """ + # remove all old handlers + for handler in logger.handlers: + logger.removeHandler(handler) + handler.close() + # add the new one + new_handler = logging.FileHandler(filename=new_filepath, mode="w") + formatter = logging.Formatter('[%(asctime)s] %(levelname)-4s: %(message)s') + new_handler.setFormatter(formatter) + + logger.addHandler(new_handler) + return logger + +######## Main Function ######## +def main(): + return + +def parse_args()-> Type[run_config]: + parser = argparse.ArgumentParser(description="Run various processor tests at the top level of RISCVBusisness") + parser.add_argument("--config_file", "-c", dest="config_file", + type=str, default=DEFAULT_CONFIG_FILE, + help="Specify the config file path") + parser.add_argument("--arch", "-a", dest="arch", + type=str, default=None, + help="Specify the architecture targeted. Option(s): RV32I Default: RV32I") + parser.add_argument("--test_type", "-t", dest="test_type", + type=str, default=None, + help="Specify what type of tests to run. Option(s): asm, selfasm,c Default: selfasm") + parser.add_argument("file_names", metavar="file_names", + type=str, nargs="*", + help="Run all tests that begin with this string. Optional") + parser.add_argument("--clean", action="store_true", dest="clean", + help="Clean the cache file before running") + args = parser.parse_args() + + conf_dict = dict() + with open(args.config_file, "r") as conf_fp: + conf_dict = json.load(conf_fp) + + config = run_config(conf_dict) + if(args.arch): + config.arch = args.arch + if(args.test_type): + config.test_type = args.test_type + if(args.file_names): + # find all the files that match the pattern + print(args.file_names) + config.test_filenames = args.file_names + # get the list of test files + test_files =[] + for filename in config.test_filenames: + test_files.append(glob.glob(str(config.test_dir/filename))) + config.test_filepaths = list(itertools.chain(*test_files)) + + # process the clean flag, remove the cache file if want a clean run + if(args.clean): + try: + os.remove(config.cache_file) + except FileNotFoundError: + pass # ignore if the file is not there + + + return config + +######## Main Function ######## +if __name__ == "__main__": + print("Running Main...") + # setup the logfile + #logging.basicConfig(filename=log_filepath, mode="w", level=logging.DEBUG) + config = parse_args() + run_tests(config) + # shutdown any remaining loggers + logging.shutdown() diff --git a/scripts/config_core.py b/scripts/config_core.py index 892f5fb03..3654bf08d 100755 --- a/scripts/config_core.py +++ b/scripts/config_core.py @@ -74,7 +74,7 @@ def load_configuration(file_name): with open(file_name, 'r') as f: try: - config = yaml.load(f) + config = yaml.full_load(f) except yaml.parser.ParserError: sys.exit('Parse of '+ file_name + ' failed. Please check yml syntax') return config @@ -86,7 +86,7 @@ def add_custom_instruction_header(name, encoding, length, opcode, fptr): fptr.write("GENERATE_CUSTOM_INSTRUCTION_R_TYPE(" + name + "," + opcode + "," + str(i) + "," + funct[0:7] + "," + funct[7:10] + ")\n") elif encoding in RISC_MGMT_PARAMS['nonstandard_extensions']['encoding']: - print "Warning: Generation of C Macros for the encoding " + encoding + " is not supported." + print("Warning: Generation of C Macros for the encoding " + encoding + " is not supported.") else: err = "Error: Invalid custom instruction encoding: " + encoding sys.exit(err) diff --git a/scripts/config_core_2.py b/scripts/config_core_2.py new file mode 100755 index 000000000..892f5fb03 --- /dev/null +++ b/scripts/config_core_2.py @@ -0,0 +1,230 @@ +#!/usr/local/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: config_core.py +# +# Created by: Jacob R. Stevens +# Email: steven69@purdue.edu +# Date Created: 02/14/2017 +# Description: Configures a RISCV core based on the given YAML config file +import yaml +import argparse +import sys + +VH_FILE = 'source_code/include/component_selection_defines.vh' +C_FILE = 'verification/c-firmware/custom_instruction_calls.h' + +ISA_PARAMS = \ + { + 'xlen' : [32] + } + +UARCH_PARAMS = \ + { + # Branch/Jump Configurations + 'br_predictor_type' : ['not_taken'], + # Cache Configurations + 'cache_config' : ['separate'], + 'dcache_type' : ['pass_through', 'direct_mapped_tpf'], + 'icache_type' : ['pass_through', 'direct_mapped_tpf'], + # Bus Configurations + 'bus_endianness' : ['big', 'little'], + 'bus_interface_type' : ['ahb_if', 'generic_bus_if'], + # Sparisty Optimizations + 'sparce_enabled' : [ 'enabled', 'disabled' ], + # RV32C + 'rv32c_enabled' : [ 'enabled', 'disabled' ], + # base ISA Configurations + 'base_isa': ['RV32I', 'RV32E'], + + # Halt Enable -- Good for testing, not for tapeout + 'infinite_loop_halts' : ['true', 'false'] + } + +RISC_MGMT_PARAMS = \ + { + # Valid standard extensions + 'standard_extensions' : {'name' : ['rv32m']}, + # Valid nonstandard extensions + 'nonstandard_extensions' : {'encoding' : ['R_TYPE', 'M_TYPE', 'J_TYPE', 'BR_TYPE', 'G_TYPE']} + } + +NONSTANDARD_OPCODES = [ '0001011', + '0101011', + '1011011', + '1111011'] + +# Returns an object containing the parsed configuration file. +# Currently uses PyYAML and YAML format +def load_configuration(file_name): + with open(file_name, 'r') as f: + try: + config = yaml.load(f) + except yaml.parser.ParserError: + sys.exit('Parse of '+ file_name + ' failed. Please check yml syntax') + return config + +def add_custom_instruction_header(name, encoding, length, opcode, fptr): + if encoding == 'R_TYPE': + for i in range(length): + funct = format(i, '010b') + fptr.write("GENERATE_CUSTOM_INSTRUCTION_R_TYPE(" + + name + "," + opcode + "," + str(i) + "," + funct[0:7] + "," + funct[7:10] + ")\n") + elif encoding in RISC_MGMT_PARAMS['nonstandard_extensions']['encoding']: + print "Warning: Generation of C Macros for the encoding " + encoding + " is not supported." + else: + err = "Error: Invalid custom instruction encoding: " + encoding + sys.exit(err) + +# Creates the include file from the config object +def create_include(config): + include_file = open(VH_FILE, 'w') + c_file = open(C_FILE, 'w') + # Write include header to file + header = '/*\nWARNING: THIS FILE WAS AUTOMATICALLY GENERATED BY\n' + header += 'CONFIG_CORE. DO NOT MAKE CHANGES TO THIS FILE. ANY CHANGES\n' + header += 'MAY BE OVERWRITTEN. DO NOT VERSION CONTROL THIS FILE.\n*/\n' + header += '`ifndef COMPONENT_SELECTION_DEFINES_VH\n' + header += '`define COMPONENT_SELECTION_DEFINES_VH\n\n\n' + include_file.write(header) + + # Write include to c file + c_header = '/*\nWARNING: THIS FILE WAS AUTOMATICALLY GENERATED BY\n' + c_header += 'CONFIG_CORE. DO NOT MAKE CHANGES TO THIS FILE. ANY CHANGES\n' + c_header += 'MAY BE OVERWRITTEN. DO NOT VERSION CONTROL THIS FILE.\n*/\n' + c_header += '#ifndef CUSTOM_INSTRUCTION_CALLS_H\n' + c_header += '#define CUSTOM_INSTRUCTION_CALLS_H\n\n\n' + c_header += '#include "custom_instruction_macros.h"\n\n' + c_header += '// Custom Instruction Usage:\n// CALL_CUSTOM_INSTRUCTION_R_TYPE(, , , , )\n\n' + c_file.write(c_header) + + # Handle localparam configurations + isa_params = config['isa_params'] + include_file.write('// ISA Params:\n') + for isa_param in isa_params: + try: + if isa_params[isa_param] not in ISA_PARAMS[isa_param]: + err = 'Illegal configuration. ' + isa_params[isa_param] + err += ' is not a valid configuration for ' + isa_param + sys.exit(err) + else: + line = 'localparam ' + # xlen will be an integer in include file, so no quotes needed + if 'xlen' == isa_param: + line += isa_param.upper() + ' = ' + str(isa_params[isa_param]) + else: + line += isa_param.upper() + ' = "' + isa_params[isa_param] + '"' + line += ';\n' + include_file.write(line) + except TypeError: + sys.exit('Type Error. Please check to make sure .yml file is correct.') + include_file.write('\n// Microarch Params:\n') + uarch_params = config['microarch_params'] + for uarch_param in uarch_params: + if uarch_params[uarch_param] not in UARCH_PARAMS[uarch_param]: + err = 'Illegal configuration. ' + uarch_params[uarch_param] + err += ' is not a valid configuration for ' + uarch_param + sys.exit(err) + else: + line = 'localparam ' + line += uarch_param.upper() + ' = "' + uarch_params[uarch_param] + '"' + line += ';\n' + include_file.write(line) + + # Handle bus interface define + bus_type = uarch_params['bus_interface_type'].split('_if')[0] + bus_define = '`define BUS_INTERFACE_' + bus_type.upper() + '\n' + include_file.write(bus_define) + + # Handling of RISC-MGMT Extensions + rmgmt_extensions = [] + try: + if 'risc_mgmt_params' in config: + rmgmt_params = config['risc_mgmt_params'] + if rmgmt_params != None: + for rmgmt_param in rmgmt_params: + extensions = rmgmt_params[rmgmt_param] + if extensions != None: + for extension in extensions: + if rmgmt_param == "standard_extensions" and extension['name'] not in RISC_MGMT_PARAMS[rmgmt_param]['name']: + err = 'Unsupported extension: ' + extension['name'] + sys.exit(err) + else: + rmgmt_extensions.append([extension, rmgmt_param]) + except: + err = "Error Parsing RISC-MGMT extension configuration." + sys.exit(err) + + # Need to at least have the nop extension + if(len(rmgmt_extensions) == 0): + rmgmt_extensions.append([{'name':'template', 'encoding' : 'R_TYPE', 'length':1}, 'nonstandard_extension']) + + include_file.write('\n// RISC-MGMT Extensions:\n') + include_file.write('`define NUM_EXTENSIONS ' + str(len(rmgmt_extensions)) + '\n') + include_file.write('`define RISC_MGMT_EXTENSIONS\t\\\n') + ext_num = 0 + nonstandard_num = 0 + for extension in rmgmt_extensions: + if(extension[1] == 'standard_extensions'): + include_file.write('\t`ADD_EXTENSION('+extension[0]['name']+','+str(ext_num)+")") + elif (nonstandard_num < len(NONSTANDARD_OPCODES)): + include_file.write('\t`ADD_EXTENSION_WITH_OPCODE('+extension[0]['name']+','+ + str(ext_num)+','+ "7'b" + NONSTANDARD_OPCODES[nonstandard_num]+")") + if 'encoding' in extension[0].keys() and 'length' in extension[0].keys(): + add_custom_instruction_header(extension[0]['name'], extension[0]['encoding'], + extension[0]['length'], NONSTANDARD_OPCODES[nonstandard_num], c_file) + else: + err = "Error: Missing the encoding or lenght field for nonstandard extension" + sys.exit(err) + nonstandard_num = nonstandard_num + 1 + ext_num = ext_num + 1 + if(ext_num != len(rmgmt_extensions)): + include_file.write('\t\\\n') + else: + include_file.write('\n') + + #set defines to indicate what ISA support is present + include_file.write('\n') + for extension in rmgmt_extensions: + if(extension[1] == 'standard_extensions'): + include_file.write('`define ' + extension[0]['name'].upper() + '_SUPPORTED\t1\n') + + # Write include footer to file + footer = '\n`endif // COMPONENT_SELECTION_DEFINES_VH\n' + include_file.write(footer) + include_file.close() + + footer = '\n#endif // CUSTOM_INSTRUCTION_CALLS_H\n' + c_file.write(footer) + c_file.close() + + + +if __name__ == '__main__': + description = 'Configure a processor. This script takes a .yml' + description += ' configuration file as input and creates the file ' + description += 'component_selection_defines.vh in source_code/include.' + description += 'Caution: if this file already exists, it is overwritten.' + parser = argparse.ArgumentParser(description=description) + parser.add_argument('file_name', metavar='file_name', type=str, + help='The config file name') + args = parser.parse_args() + + #TODO: Check for .yaml/.yml + config = load_configuration(args.file_name) + create_include(config) diff --git a/source_code/caches/separate_caches.sv b/source_code/caches/separate_caches.sv index 9a7a87448..760b8be6c 100644 --- a/source_code/caches/separate_caches.sv +++ b/source_code/caches/separate_caches.sv @@ -35,7 +35,9 @@ module separate_caches ( cache_control_if.caches cc_if ); generate + /* verilator lint_off width */ case (DCACHE_TYPE) + /* verilator lint_on width */ "pass_through" : begin pass_through_cache dcache( .CLK(CLK), @@ -60,7 +62,9 @@ module separate_caches ( endgenerate generate + /* verilator lint_off width */ case (ICACHE_TYPE) + /* verilator lint_on width */ "pass_through" : begin pass_through_cache icache( .CLK(CLK), diff --git a/source_code/include/core_interrupt_if.vh b/source_code/include/core_interrupt_if.vh index bd3217734..53558945c 100644 --- a/source_code/include/core_interrupt_if.vh +++ b/source_code/include/core_interrupt_if.vh @@ -1,26 +1,26 @@ `ifndef CORE_INTERRUPT_IF `define CORE_INTERRUPT_IF - interface core_interrupt_if(); - logic ext_int, ext_int_clear; - logic soft_int, soft_int_clear; - logic timer_int, timer_int_clear; +interface core_interrupt_if (); + logic ext_int, ext_int_clear; + logic soft_int, soft_int_clear; + logic timer_int, timer_int_clear; - modport core( - input ext_int, soft_int, timer_int, - ext_int_clear, soft_int_clear, timer_int_clear - ); + modport core( + input ext_int, soft_int, timer_int, + ext_int_clear, soft_int_clear, timer_int_clear + ); - modport plic( - output ext_int, ext_int_clear - ); + modport plic( + output ext_int, ext_int_clear + ); - modport clint( - output soft_int_clear, timer_int_clear, - soft_int, timer_int - ); + modport clint( + output soft_int_clear, timer_int_clear, + soft_int, timer_int + ); - endinterface +endinterface `endif diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index 84d777688..ed2d63e1e 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -150,7 +150,7 @@ module tspp_execute_stage( /******************************************************* *** Jump Target Calculator and Associated Logic *******************************************************/ - word_t jump_addr; + word_t jump_addr /* verilator isolate_assignments */; always_comb begin if (cu_if.j_sel) begin jump_if.base = fetch_ex_if.fetch_ex_reg.pc; diff --git a/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv b/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv index 0a8a52fcc..127558fc8 100644 --- a/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv +++ b/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv @@ -63,51 +63,52 @@ module priv_1_11_csr_rfile ( mideleg_t mideleg; mie_t mie, mie_next; mtvec_t mtvec, mtvec_next; - +/* // Privilege and Global Interrupt-Enable Stack - assign mstatus.uie = 1'b0; - assign mstatus.sie = 1'b0; - assign mstatus.reserved_0 = 1'b0; - assign mstatus.upie = 1'b0; - assign mstatus.spie = 1'b0; - assign mstatus.reserved_1 = 1'b0; - assign mstatus.spp = 1'b0; - assign mstatus.reserved_2 = 2'b0; - assign mstatus.mpp = M_LEVEL; + assign mstatus_next.uie = 1'b0; + assign mstatus_next.sie = 1'b0; + assign mstatus_next.reserved_0 = 1'b0; + assign mstatus_next.upie = 1'b0; + assign mstatus_next.spie = 1'b0; + assign mstatus_next.reserved_1 = 1'b0; + assign mstatus_next.spp = 1'b0; + assign mstatus_next.reserved_2 = 2'b0; + assign mstatus_next.mpp = M_LEVEL; // No memory protection - assign mstatus.mprv = 1'b0; - assign mstatus.sum = 1'b0; - assign mstatus.mxr = 1'b0; + assign mstatus_next.mprv = 1'b0; + assign mstatus_next.sum = 1'b0; + assign mstatus_next.mxr = 1'b0; // No virtualization protection - assign mstatus.tvm = 1'b0; - assign mstatus.tw = 1'b0; - assign mstatus.tsr = 1'b0; + assign mstatus_next.tvm = 1'b0; + assign mstatus_next.tw = 1'b0; + assign mstatus_next.tsr = 1'b0; // No FPU or Extensions - assign mstatus.xs = XS_ALL_OFF; - assign mstatus.fs = FS_OFF; // Even though FPU will be integrated for AFTx06, there is no functionality for Supervisor Mode - assign mstatus.sd = (mstatus.fs == FS_DIRTY) | (mstatus.xs == XS_SOME_D); - assign mstatus.reserved_3 = '0; - + assign mstatus_next.xs = XS_ALL_OFF; + assign mstatus_next.fs = FS_OFF; // Even though FPU will be integrated for AFTx06, there is no functionality for Supervisor Mode + assign mstatus_next.sd = (mstatus.fs == FS_DIRTY) | (mstatus.xs == XS_SOME_D); + assign mstatus_next.reserved_3 = '0; +*/ // Deleg Register Zero in Machine Mode Only (Should be removed) assign medeleg = '0; assign mideleg = '0; - assign mie.reserved_0 = '0; - assign mie.reserved_1 = '0; - assign mie.reserved_2 = '0; - assign mie.reserved_3 = '0; - assign mie.utie = 1'b0; - assign mie.stie = 1'b0; - assign mie.usie = 1'b0; - assign mie.ssie = 1'b0; - assign mie.ueie = 1'b0; - assign mie.seie = 1'b0; - +/* + assign mie_next.reserved_0 = '0; + assign mie_next.reserved_1 = '0; + assign mie_next.reserved_2 = '0; + assign mie_next.reserved_3 = '0; + assign mie_next.utie = 1'b0; + assign mie_next.stie = 1'b0; + assign mie_next.usie = 1'b0; + assign mie_next.ssie = 1'b0; + assign mie_next.ueie = 1'b0; + assign mie_next.seie = 1'b0; +*/ /* Machine Trap Handling */ mscratch_t mscratch, mscratch_next; @@ -115,18 +116,18 @@ module priv_1_11_csr_rfile ( mcause_t mcause, mcause_next; mtval_t mtval, mtval_next; mip_t mip, mip_next; - - assign mip.reserved_0 = '0; - assign mip.reserved_1 = '0; - assign mip.reserved_2 = '0; - assign mip.reserved_3 = '0; - assign mip.utip = 1'b0; - assign mip.stip = 1'b0; - assign mip.usip = 1'b0; - assign mip.ssip = 1'b0; - assign mip.ueip = 1'b0; - assign mip.seip = 1'b0; - +/* + assign mip_next.reserved_0 = '0; + assign mip_next.reserved_1 = '0; + assign mip_next.reserved_2 = '0; + assign mip_next.reserved_3 = '0; + assign mip_next.utip = 1'b0; + assign mip_next.stip = 1'b0; + assign mip_next.usip = 1'b0; + assign mip_next.ssip = 1'b0; + assign mip_next.ueip = 1'b0; + assign mip_next.seip = 1'b0; +*/ /* Machine Counter Delta Registers */ // Unimplemented, only Machine Mode Supported @@ -151,8 +152,9 @@ module priv_1_11_csr_rfile ( always_ff @ (posedge CLK, negedge nRST) begin if (~nRST) begin - mstatus.mie <= 1'b0; - mstatus.mpie <= 1'b0; + mstatus <= '0; + //mstatus.mie <= 1'b0; + //mstatus.mpie <= 1'b0; mie.mtie <= 1'b0; mie.msie <= 1'b0; mip.msip <= 1'b0; @@ -168,14 +170,17 @@ module priv_1_11_csr_rfile ( cyclefull <= '0; instretfull <= '0; end else begin - mstatus.mie <= mstatus_next.mie; - mstatus.mpie <= mstatus_next.mpie; - mie.mtie <= mie_next.mtie; - mie.msie <= mie_next.msie; - mie.meie <= mie_next.meie; - mip.msip <= mip_next.msip; - mip.mtip <= mip_next.mtip; - mip.meip <= mip_next.meip; + mstatus <= mstatus_next; + //mstatus.mie <= mstatus_next.mie; + //mstatus.mpie <= mstatus_next.mpie; + mie <= mie_next; + //mie.mtie <= mie_next.mtie; + //mie.msie <= mie_next.msie; + //mie.meie <= mie_next.meie; + mip <= mip_next; + //mip.msip <= mip_next.msip; + //mip.mtip <= mip_next.mtip; + //mip.meip <= mip_next.meip; misaid <= misaid_next; mtvec <= mtvec_next; mcause <= mcause_next; diff --git a/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv b/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv index 45a671fb3..f46a6e082 100644 --- a/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv +++ b/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv @@ -36,7 +36,7 @@ module priv_1_11_pipeline_control always_comb begin - prv_intern_if.priv_pc = 'Z; + prv_intern_if.priv_pc = '0; if(prv_intern_if.intr) begin if (prv_intern_if.mtvec.mode == VECTORED & prv_intern_if.mcause.interrupt) begin // vectored mode based on the interrupt source diff --git a/source_code/rv32c/fetch_buffer.sv b/source_code/rv32c/fetch_buffer.sv index c70c853b8..c5cff08d2 100644 --- a/source_code/rv32c/fetch_buffer.sv +++ b/source_code/rv32c/fetch_buffer.sv @@ -26,7 +26,7 @@ module fetch_buffer ( input logic clk, n_rst, - fetch_buffer.fb fb_if + fetch_buffer_if.fb fb_if ); logic [15:0] buffer, nextbuffer; logic [31:0] pc, next_imem_pc; @@ -37,7 +37,7 @@ module fetch_buffer always_ff @ (posedge clk, negedge n_rst) begin if (n_rst == 0) inst_arrived_delay <= 1'b0; - else inst_arrived_delay <= fb_if.inst_arrived; + else inst_arrived_delay <= fb_if.inst_arrived; end @@ -47,92 +47,92 @@ module fetch_buffer // Buffer and PC logic always_ff @ (posedge clk, negedge n_rst) begin if (n_rst == 0) begin - buffer <= 16'd0; - combine_reg <= 1'b0; - waitnext_reg <= 1'b0; - fb_if.imem_pc <= fb_if.reset_pc_val; - pc <= fb_if.reset_pc_val; - final_inst_store <= 32'd0; - reset_next <= 1'b0; - end else if (fb_if.reset_en) begin - buffer <= nextbuffer; - combine_reg <= combine; - waitnext_reg <= waitnext; - fb_if.imem_pc <= next_imem_pc; - pc <= fb_if.nextpc; - final_inst_store <= final_inst; - reset_next <= 1'b1; - end else if (fb_if.inst_arrived | fb_if.done_earlier) begin - buffer <= nextbuffer; - combine_reg <= combine; - waitnext_reg <= waitnext; - fb_if.imem_pc <= next_imem_pc; - pc <= fb_if.nextpc; - final_inst_store <= final_inst; - reset_next <= 1'b0; - end + buffer <= 16'd0; + combine_reg <= 1'b0; + waitnext_reg <= 1'b0; + fb_if.imem_pc <= fb_if.reset_pc_val; + pc <= fb_if.reset_pc_val; + final_inst_store <= 32'd0; + reset_next <= 1'b0; + end else if (fb_if.reset_en) begin + buffer <= nextbuffer; + combine_reg <= combine; + waitnext_reg <= waitnext; + fb_if.imem_pc <= next_imem_pc; + pc <= fb_if.nextpc; + final_inst_store <= final_inst; + reset_next <= 1'b1; + end else if (fb_if.inst_arrived | fb_if.done_earlier) begin + buffer <= nextbuffer; + combine_reg <= combine; + waitnext_reg <= waitnext; + fb_if.imem_pc <= next_imem_pc; + pc <= fb_if.nextpc; + final_inst_store <= final_inst; + reset_next <= 1'b0; + end end always_comb begin next_imem_pc = fb_if.imem_pc; - fb_if.nextpc = pc; - nextbuffer = buffer; - combine = 1'b0; - waitnext = 1'b0; - final_inst = final_inst_store; - finished = 1'b0; - if (fb_if.inst_arrived & reset_next & (pc != fb_if.imem_pc)) begin // Handle Jump/Branch Condition when misaligned - next_imem_pc = fb_if.imem_pc + 4; - if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed - final_inst = {16'd0, fb_if.inst[31:16]}; + fb_if.nextpc = pc; + nextbuffer = buffer; + combine = 1'b0; + waitnext = 1'b0; + final_inst = final_inst_store; + finished = 1'b0; + if (fb_if.inst_arrived & reset_next & (pc != fb_if.imem_pc)) begin // Handle Jump/Branch Condition when misaligned + next_imem_pc = fb_if.imem_pc + 4; + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + final_inst = {16'd0, fb_if.inst[31:16]}; fb_if.nextpc = pc + 2; - finished = 1'b1; - end else begin - combine = 1; - nextbuffer = fb_if.inst[31:16]; -// nextpc = pc + 4; - finished = 1'b0; - end - end else if (fb_if.reset_en) begin - next_imem_pc = {fb_if.reset_pc[31:2], 2'b0}; // Always aligned - fb_if.nextpc = fb_if.reset_pc; // Can be misaligned - nextbuffer = 16'd0; - final_inst = 32'd0; - end else if (fb_if.inst_arrived & combine_reg) begin - final_inst = {fb_if.inst[15:0], buffer}; - nextbuffer = fb_if.inst[31:16]; - fb_if.nextpc = pc + 4; - finished = 1'b1; - if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed - waitnext = 1; - next_imem_pc = fb_if.imem_pc; - end else begin - combine = 1; - next_imem_pc = fb_if.imem_pc + 4; - end + finished = 1'b1; + end else begin + combine = 1; + nextbuffer = fb_if.inst[31:16]; + // nextpc = pc + 4; + finished = 1'b0; + end + end else if (fb_if.reset_en) begin + next_imem_pc = {fb_if.reset_pc[31:2], 2'b0}; // Always aligned + fb_if.nextpc = fb_if.reset_pc; // Can be misaligned + nextbuffer = 16'd0; + final_inst = 32'd0; + end else if (fb_if.inst_arrived & combine_reg) begin + final_inst = {fb_if.inst[15:0], buffer}; + nextbuffer = fb_if.inst[31:16]; + fb_if.nextpc = pc + 4; + finished = 1'b1; + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + waitnext = 1; + next_imem_pc = fb_if.imem_pc; + end else begin + combine = 1; + next_imem_pc = fb_if.imem_pc + 4; + end end else if (fb_if.done_earlier | (fb_if.inst_arrived & waitnext_reg)) begin final_inst = {16'b0, buffer}; - finished = 1'b1; - fb_if.nextpc = pc + 2; - next_imem_pc = fb_if.imem_pc + 4; - end else if (fb_if.inst[1:0] != 2'b11) begin // lower 16 bits are compressed - final_inst = fb_if.inst[15:0]; - nextbuffer = fb_if.inst[31:16]; + finished = 1'b1; fb_if.nextpc = pc + 2; - finished = 1'b1; - if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed - waitnext = 1; - next_imem_pc = fb_if.imem_pc; - end else begin - combine = 1; - next_imem_pc = fb_if.imem_pc + 4; - end + next_imem_pc = fb_if.imem_pc + 4; + end else if (fb_if.inst[1:0] != 2'b11) begin // lower 16 bits are compressed + final_inst = fb_if.inst[15:0]; + nextbuffer = fb_if.inst[31:16]; + fb_if.nextpc = pc + 2; + finished = 1'b1; + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + waitnext = 1; + next_imem_pc = fb_if.imem_pc; + end else begin + combine = 1; + next_imem_pc = fb_if.imem_pc + 4; + end end else if (fb_if.inst[1:0] == 2'b11) begin - final_inst = fb_if.inst; - next_imem_pc = fb_if.imem_pc + 4; - fb_if.nextpc = pc + 4; - finished = 1'b1; - end + final_inst = fb_if.inst; + next_imem_pc = fb_if.imem_pc + 4; + fb_if.nextpc = pc + 4; + finished = 1'b1; + end end //logic [31:0] debug; diff --git a/source_code/rv32c/rv32c_wrapper.sv b/source_code/rv32c/rv32c_wrapper.sv index 283eae09e..f0b4997f8 100644 --- a/source_code/rv32c/rv32c_wrapper.sv +++ b/source_code/rv32c/rv32c_wrapper.sv @@ -7,7 +7,9 @@ module rv32c_wrapper( ); generate + /* verilator lint_off width */ case(RV32C_ENABLED) + /* verilator lint_on width */ "disabled" : rv32c_disabled RV32C(.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); "enabled" : rv32c_enabled RV32C(.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); endcase diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index f5cadb96e..50519b349 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -35,7 +35,7 @@ module RISCVBusiness ( input logic CLK, nRST, - output logic wfi, + output logic wfi, halt, core_interrupt_if.core interrupt_if, `ifdef BUS_INTERFACE_GENERIC_BUS generic_bus_if.cpu gen_bus_if @@ -44,6 +44,8 @@ module RISCVBusiness ( `endif ); + parameter RESET_PC = 32'h80000000; + // Interface instantiations generic_bus_if tspp_icache_gen_bus_if(); @@ -62,8 +64,6 @@ module RISCVBusiness ( tspp_fetch_execute_if fetch_ex_if(); tspp_hazard_unit_if hazard_if(); - logic halt; //JOHN CHANGED THIS - // Module Instantiations /* pipeline_wrapper pipeline ( @@ -80,7 +80,9 @@ module RISCVBusiness ( ); */ - tspp_fetch_stage fetch_stage_i ( + tspp_fetch_stage #( + .RESET_PC(RESET_PC) + ) fetch_stage_i ( .CLK(CLK), .nRST(nRST), .fetch_ex_if(fetch_ex_if), diff --git a/source_code/standard_core/top_core.sv b/source_code/standard_core/top_core.sv new file mode 100644 index 000000000..0d89e2fe6 --- /dev/null +++ b/source_code/standard_core/top_core.sv @@ -0,0 +1,83 @@ +`include "core_interrupt_if.vh" +`include "ahb_if.vh" +`include "generic_bus_if.vh" + +module top_core #( + parameter RESET_PC = 32'h80000000 +)( + input CLK, nRST, + output wfi, halt, + // core_interrupt_if + input ext_int, ext_int_clear, + input soft_int, soft_int_clear, + input timer_int, timer_int_clear, + // generic bus if case + `ifdef BUS_INTERFACE_GENERIC_BUS + input busy, + input [31:0] rdata, + output ren, wen, + output [3:0] byte_en, + output [31:0] addr, wdata + // ahb if case + `elsif BUS_INTERFACE_AHB + // TODO + `endif +); + + + function [31:0] get_x28; + // verilator public + get_x28 = CORE.execute_stage_i.REG_FILE_SEL.rf.registers[28]; + endfunction + + bind tspp_execute_stage cpu_tracker cpu_track1 ( + .CLK(CLK), + .wb_stall(wb_stall), + .instr(fetch_ex_if.fetch_ex_reg.instr), + .pc(fetch_ex_if.fetch_ex_reg.pc), + .opcode(cu_if.opcode), + .funct3(funct3), + .funct12(funct12), + .rs1(rf_if.rs1), + .rs2(rf_if.rs2), + .rd(rf_if.rd), + .imm_S(cu_if.imm_S), + .imm_I(cu_if.imm_I), + .imm_U(cu_if.imm_U), + .imm_UJ(imm_UJ_ext), + .imm_SB(cu_if.imm_SB), + .instr_30(instr_30) + ); + + + + core_interrupt_if interrupt_if(); + assign interrupt_if.ext_int = ext_int; + assign interrupt_if.ext_int_clear = ext_int_clear; + assign interrupt_if.soft_int = soft_int; + assign interrupt_if.soft_int_clear = soft_int_clear; + assign interrupt_if.timer_int = timer_int; + assign interrupt_if.timer_int_clear = timer_int_clear; + + `ifdef BUS_INTERFACE_GENERIC_BUS + generic_bus_if gen_bus_if(); + assign gen_bus_if.busy = busy; + assign gen_bus_if.rdata = rdata; + assign ren = gen_bus_if.ren; + assign wen = gen_bus_if.wen; + assign byte_en = gen_bus_if.byte_en; + assign addr = gen_bus_if.addr; + assign wdata = gen_bus_if.wdata; + `elsif BUS_INTERFACE_AHB + ahb_if ahb_master(); + // TODO + `endif + + + RISCVBusiness #( + .RESET_PC(RESET_PC) + ) CORE( + .* + ); + +endmodule diff --git a/tb_core.cc b/tb_core.cc new file mode 100644 index 000000000..6d4291470 --- /dev/null +++ b/tb_core.cc @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include + +#include "verilated.h" +#include "verilated_fst_c.h" +#include "Vtop_core.h" +#include "Vtop_core_top_core.h" + +vluint64_t sim_time = 0; + + + +class MemoryMap { +private: + + // NOTE: Assuming 0 for uninitialized memory + // This is because bare-metal tests may not set up bss, + // but the program will nonetheless have a bss section. + const uint32_t c_default_value = 0x00000000; + const char *dumpfile = "memsim.dump"; + std::map mmap; + +protected: + inline uint32_t expand_mask(uint8_t mask) { + uint32_t acc = 0; + for(int i = 0; i < 4; i++) { + auto bit = ((mask & (1 << i)) != 0); + if(bit) { + acc |= (0xFF << (i * 8)); + } + } + + return acc; + } + +public: + + MemoryMap(const char *fname) { + uint32_t address = 0x80000000; + std::ifstream myFile(fname, std::ios::in | std::ios::binary); + if(!myFile) { + std::ostringstream ss; + ss << "Couldn't open " << fname << std::endl; + throw ss.str(); + } + + while(!myFile.eof()) { + uint32_t data; + myFile.read((char *)&data, sizeof(data)); + + mmap.insert(std::make_pair(address, data)); + + address += 4; + } + } + + // TODO: Add simulation for SWI/mtime? + uint32_t read(uint32_t addr) { + auto it = mmap.find(addr); + if(it != mmap.end()) { + return it->second; + } else { + return c_default_value; + } + } + + void write(uint32_t addr, uint32_t value, uint8_t mask) { + // NOTE: For now, assuming that all memory is legally acessible. + auto it = mmap.find(addr); + if(it != mmap.end()) { + auto mask_exp = expand_mask(mask); + it->second = (value & mask_exp) | (it->second & ~mask_exp); + } else { + mmap.insert(std::make_pair(addr, value)); + } + } + + void dump() { + std::ofstream outfile; + outfile.open(dumpfile); + if(!outfile) { + std::ostringstream ss; + ss << "Couldn't open " << dumpfile << std::endl; + throw ss.str(); + } + + // Account for endianness + for(auto p : mmap) { + if(p.second != 0) { + char buf[80]; + snprintf(buf, 80, "%08x : %02x%02x%02x%02x", p.first, + (p.second & 0xFF000000) >> 24, + (p.second & 0x00FF0000) >> 16, + (p.second & 0x0000FF00) >> 8, + p.second & 0x000000FF); + outfile << buf << std::endl; + } + } + } +}; + +void tick(Vtop_core& dut, VerilatedFstC& trace) { + dut.CLK = 0; + dut.eval(); + trace.dump(sim_time); + sim_time++; + dut.CLK = 1; + dut.eval(); + trace.dump(sim_time); + sim_time++; +} + +void reset(Vtop_core& dut, VerilatedFstC& trace) { + // Initialize signals + dut.CLK = 0; + dut.nRST = 0; + dut.ext_int = 0; + dut.ext_int_clear = 0; + dut.soft_int = 0; + dut.soft_int_clear = 0; + dut.timer_int = 0; + dut.timer_int_clear = 0; + dut.busy = 1; + dut.rdata = 0; + + tick(dut, trace); + dut.nRST = 0; + tick(dut, trace); + dut.nRST = 1; + tick(dut, trace); +} + + +int main(int argc, char **argv) { + + const char *fname; + + if(argc < 2) { + std::cout << "Warning: No bin file name provided, assuming './meminit.bin' as file location!" << std::endl; + fname = "meminit.bin"; + } else { + fname = argv[1]; + } + + MemoryMap memory(fname); + + Vtop_core dut; + + Verilated::traceEverOn(true); + VerilatedFstC m_trace; + dut.trace(&m_trace, 5); + m_trace.open("waveform.fst"); + + + reset(dut, m_trace); + while(!dut.halt && sim_time < 1000000) { + // TODO: Variable latency + if((dut.ren || dut.wen) && dut.busy) { + dut.busy = 0; + if(dut.ren) { + uint32_t addr = dut.addr & 0xFFFFFFFC; + dut.rdata = memory.read(addr); + } else if(dut.wen) { + uint32_t addr = dut.addr & 0xFFFFFFFC; + uint32_t value = dut.wdata; + uint8_t mask = dut.byte_en; + memory.write(addr, value, mask); + } + } else if(!dut.busy) { + dut.busy = 1; + } + + tick(dut, m_trace); + } + + if(sim_time == 1000000) { + std::cout << "Test TIMED OUT" << std::endl; + } else if(dut.top_core->get_x28() == 1) { + std::cout << "Test PASSED" << std::endl; + } else { + std::cout << "Test FAILED: Test " << dut.top_core->get_x28() << std::endl; + } + m_trace.close(); + memory.dump(); + + return 0; +} diff --git a/updated_run_tests.py b/updated_run_tests.py new file mode 100644 index 000000000..3371a3133 --- /dev/null +++ b/updated_run_tests.py @@ -0,0 +1,435 @@ +#!/usr/bin/python3 + +# +# Copyright 2022 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: updated_run_tests.py +# +# Created by: Nicholas Gildenhuys +# Email: ngildenh@purdue.edu +# Date Created: 03/29/2022 +# Description: A better script for running processor tests + + +######## Library Imports ######## +import argparse +from asyncio.log import logger +import sys +import os +import re +from typing import List, Type +import logging +import subprocess +import pathlib +import glob +import json +import itertools + + +######## Globals ######## +END_COLOR = "\033[0m" +GREEN = "\033[92m" +RED = "\033[31m" +#WAF_LOGFILE = "waf_output.log" +#BUILD_LOGFILE = "build_log.log" +DEFAULT_CONFIG_FILE = "run_tests_config.json" +MEMINIT_HEX_FILE = "meminit.hex" +ELF2HEX_COMAND = "/home/ecegrid/a/socpub/Public/riscv_dev/riscv_installs/RV_current/bin/elf2hex" +FAILED = "Failed" +PASSED = "Passed" + +######## Error Code Exception Class ######## +class Error(Exception): + """A custom exception class for raising exceptions from sub processes""" + def __init__(self, error_string: str): + self.error_string = error_string + def __str__(self) -> str: + return self.error_string + +class run_config(): + def __init__(self, config_json:dict): + # top level info + self.arch = str(config_json["arch"]) + self.abi = str(config_json["abi"]) + self.xlen = str(config_json["xlen"]) + self.test_type = str(config_json["test_type"]) + self.top_level = str(config_json["top_level"]) + # read directory definitions + self.build_dir = pathlib.Path(config_json["build_dir"]) + self.verif_dir = pathlib.Path(config_json["verif_dir"]) + self.asm_env = pathlib.Path(config_json["asm_env"]) + # directory of all where the test files are + self.test_dir = self.verif_dir/self.test_type/self.arch + + # output directories + self.sim_dir = pathlib.Path(config_json["sim_dir"]) + self.out_dir = self.sim_dir/self.arch + + # file definitions + self.link_file = self.verif_dir/pathlib.Path("asm-env")/config_json["link_file"] + # the cache file name, expects it in same as run directory + self.cache_file = pathlib.Path(config_json["cache_file"]) + # location of the test files + if(not self.test_dir.exists()): + raise Error(f"Test Directory: {self.test_dir} does not exists") + # init for apending later + self.test_filepaths = [] + self.test_filenames = config_json["test_filenames"] + return + +######## Bulk Work Functions ######## +def run_tests(config: Type[run_config]) -> List[str]: + """ + Should return the list of tests that were failed + """ + print(f"Running Strings: {config.test_filepaths}") + # setup the sim out directory + if(not config.out_dir.exists()): + os.makedirs(config.out_dir) + + # setup the loggers + # the log names don't matter as they will be replaced in the for loop + build_logger = setup_logger("build_logger", pathlib.Path("./build_log.log")) + waf_logger = setup_logger("waf_logger", pathlib.Path("./waf_log.log")) + + # setup caching dict mechanism + test_status = dict() + # read in the cached results if possible + if(pathlib.Path(config.cache_file).exists()): + with open(config.cache_file, "r") as cache_fp: + test_status = json.load(cache_fp) + + # run the tests + try: + for file in config.test_filepaths: + filepath = pathlib.Path(file) + # check if there is a cached result + test_cached_result = FAILED + if(filepath.stem in test_status): + test_cached_result = test_status[filepath.stem] + + # skip files that begin with _ + # or files that have already passed + if(filepath.name[0] == '_' or test_cached_result == PASSED): + print(f"Skipping Test: {filepath.name}") + continue + print(f"-----------------------------") + print(f"Running Test: {filepath.name}") + # setup the test output directory if it is not there + test_out_dir = config.out_dir/filepath.stem + # setup the sim out folder + if(not test_out_dir.exists()): + os.makedirs(test_out_dir) + # update the loggers + build_log_filepath = test_out_dir/str(filepath.stem+"_build.log") + build_logger = change_logger_file_handlers(build_logger, build_log_filepath) + waf_log_filepath = test_out_dir/str(filepath.stem+"_waf.log") + waf_logger = change_logger_file_handlers(waf_logger, waf_log_filepath) + + # compile the assembly file + print(f" - Compiling...") + outpath = test_out_dir/str(filepath.stem+".elf") + hex_filepath = compile_asm(filepath, outpath, config, build_logger) + # clean up the hex file - needs writting + clean_init_hex(hex_filepath) + print(f" - Running Waf...") + # run self sim - basic done + run_sim(config.top_level, waf_logger) + waf_log_filepath = pathlib.Path(waf_logger.handlers[0].baseFilename) + print(f" - Checking Results...") + # check results - + result = check_self_results(waf_log_filepath, build_logger) + # cache the result in the dict + test_status[filepath.stem] = result + #print(test_status) + # catch keyboard interrupts to flush the cache file + except KeyboardInterrupt: + pass # yes I know that this is not the pest practice + + with open(config.cache_file, "w") as cache_fp: + json.dump(test_status, cache_fp) + + return + + +# compile the assembly file - done +def compile_asm(filepath: Type[pathlib.Path], outpath: Type[pathlib.Path],\ + config: Type[run_config], logger: Type[logging.Logger])\ + -> Type[pathlib.Path]: + # main compile arguments + # notes: need to parameratize the .T, .I, abi, and xlen flags + # also probably pass the filepath too + compile_cmd_arr = ["riscv64-unknown-elf-gcc", + "-march=" + config.xlen, "-mabi=" + config.abi, + "-static", "-mcmodel=medany", "-fvisibility=hidden", + "-nostdlib", "-nostartfiles", + "-T"+str(config.link_file), + "-I"+str(config.asm_env), str(filepath), "-o", + str(outpath)] + + log_header("riscv64-unknown-elf-gcc", logger) + try: + compile_process: Type[subprocess.CompletedProcess] = subprocess.run(compile_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except subprocess.CalledProcessError as p_error: + compile_process = p_error + finally: + log_subprocess(compile_process, logger) + + + # create the mem init file + # NOTE: Hard coded values here that I am too lazy to config + elf_2_hex_cmd_arr = [ELF2HEX_COMAND, "8", "65536", str(outpath), "2147483648"] + # hex file return this for cleaning + hex_filepath = outpath.parent/MEMINIT_HEX_FILE + + log_header("elf2hex", logger) + elf_2_hex_process: Type[subprocess.CompletedProcess] = subprocess.run(elf_2_hex_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + log_subprocess(elf_2_hex_process, logger, level=logging.ERROR) + with open(hex_filepath, "w") as hex_file: + hex_file.write(str(elf_2_hex_process.stdout, encoding="utf-8")) + + logger.info(f"Finished {filepath.name} Compilation") + + return hex_filepath + +# run the simulation +def run_sim(top_level: str, logger: Type[logging.Logger]) -> None: + + config_cmd_arr = ["waf", "configure", "--top_level=" + top_level] + log_header("waf configure", logger) + config_process = subprocess.run(config_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + log_subprocess(config_process, logger) + + sim_cmd_arr = ["waf", "verify_source"] + log_header("waf verify_source", logger) + sim_process = subprocess.run(sim_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + log_subprocess(sim_process, logger) + + return + +def run_spike_asm(filepath: str) -> None: + return +# compare the results +def compare_results(filepath: str) -> bool: + return False +# compare spike results +def compare_spike_results(): + return +# just check selfasm results +def check_self_results(filepath: Type[pathlib.Path],\ + logger: Type[logging.Logger]) -> str: + # filepath should be path to the waf_output.log + short_name = filepath.parent.stem + pass_msg = '{0:<10}{1}'.format(short_name,GREEN + '[PASSED]' + END_COLOR) + fail_msg = '{0:<10}{1}'.format(short_name,RED + '[FAILED]' + END_COLOR) + with open(filepath, 'r') as waf_output: + waf_output_text = waf_output.read() + match = re.search(r'SUCCESS', waf_output_text) + if match: + print(pass_msg) + logger.info(pass_msg) + return PASSED + else: + match = re.search(r'(ERROR:)\s+(.*)', waf_output_text) + print(match.group(2)) + print(fail_msg) + logger.error(fail_msg) + return FAILED + +######## Side Helper Function ######## +def log_subprocess(complete_proc: Type[subprocess.CompletedProcess],\ + logger: Type[logging.Logger], level=logging.NOTSET) -> None: + """ Logs the std output to the logfile, then if there is an error + it raises an exception that prints out the output from stderr + """ + # this is mainly for the elf2hex so we dont get the hexdump in the log + # but we can still get errors + if(level <= logging.INFO): + logger.info(str(complete_proc.stdout, encoding="utf-8")) + # put in error if there are any and raise an exception + if(complete_proc.returncode): + error_string = str(complete_proc.stderr, encoding="utf-8") + logger.error(error_string) + raise Error(error_string=error_string) + return +def log_header(process: str, logger: Type[logging.Logger]) -> None: + logger.info(f"============ Starting {process} Process ============") + return + +# Create a temp file that consists of the Intel HEX format +# version of the meminit.hex file, delete the original log file +# and rename the temp file to the original's name +def clean_init_hex(filepath: Type[pathlib.Path]) -> None: + # filepath is the path object to the dirty meminit.hex file + # the one that was generated by the elf to hex + #short_name = file_name.split(ARCH+'/')[1][:-2] + #short_name = filepath.stem + #output_dir = './sim_out/' + ARCH + '/' + short_name + '/' + #output_dir = filepath.parent + #init_output = output_dir + 'meminit.hex' + init_output = filepath + build_dir = pathlib.Path("./build/") + + #cleaned_location = init_output[:len(file_name)-4] + "_clean.hex" + cleaned_location = filepath.parent/"meminit_clean.hex" + addr = 0x00 + with open(init_output, 'r') as init_file: + with open(cleaned_location, 'w')as cleaned_file: + for line in init_file: + stripped_line = line[:len(line)-1] + for i in range(len(stripped_line), 0, -8): + data_word = stripped_line[i-8:i] + new_data_word = data_word[6:8] + data_word[4:6] + new_data_word += data_word[2:4] + data_word[0:2] + checksum = calculate_checksum_str(int(new_data_word, 16), addr) + if len(checksum) < 2: + checksum = '0' + checksum + addr_str = hex(addr//4)[2:] + #left pad the string with 0s until 4 hex digits + while len(addr_str) < 4: + addr_str = '0' + addr_str + if new_data_word != "00000000": + out = ":04" + addr_str + "00" + new_data_word + checksum + '\n' + cleaned_file.write(out) + addr += 0x4 + # add the EOL record to the file + cleaned_file.write(":00000001FF") + + + subprocess.call(['mv', str(init_output), str(init_output.parent/"meminit_dirty.hex")]) + subprocess.call(['mv', str(cleaned_location), str(init_output)]) + if not os.path.exists(build_dir): + os.makedirs(build_dir) + subprocess.call(['cp', str(init_output), str(build_dir/"meminit.hex")]) + return + +# Returns the string representation of the +# checksum for the given data and address values +def calculate_checksum_str(data: int, addr: int) -> str: + addr = addr//4 + high_addr = (addr & 0xFF00) >> 8 + low_addr = addr & 0x00FF + data1 = data & 0x000000FF + data2 = (data & 0x0000FF00) >> 8 + data3 = (data & 0x00FF0000) >> 16 + data4 = (data & 0xFF000000) >> 24 + checksum = 4 + high_addr + low_addr + checksum += data1 + data2 + data3 + data4 + checksum = checksum & 0xFF + checksum = int(invert_bin_string(bin(checksum)[2:]),2) + checksum += 1 + checksum_lower_byte = hex(checksum)[2:] + if len(checksum_lower_byte) > 2: + checksum_lower_byte = checksum_lower_byte[-2:] + return checksum_lower_byte + +def invert_bin_string(bin_string: str) -> str: + inverted = '' + while len(bin_string) < 8: + bin_string = '0' + bin_string + for bit in bin_string: + if bit == '0': + inverted = inverted + '1' + else: + inverted = inverted + '0' + return inverted + +######## Logger Related Functions ######## +def setup_logger(name: str, log_filepath: Type[pathlib.Path],\ + level=logging.DEBUG) -> Type[logging.Logger]: + handler = logging.FileHandler(filename=log_filepath, mode="w") + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + logger = logging.getLogger(name) + logger.setLevel(level) + logger.addHandler(handler) + return logger + +def change_logger_file_handlers(logger: Type[logging.Logger],\ + new_filepath: Type[pathlib.Path]) -> Type[logging.Logger]: + """ Removes all old file handlers and replaces it with a new one """ + # remove all old handlers + for handler in logger.handlers: + logger.removeHandler(handler) + handler.close() + # add the new one + new_handler = logging.FileHandler(filename=new_filepath, mode="w") + formatter = logging.Formatter('[%(asctime)s] %(levelname)-4s: %(message)s') + new_handler.setFormatter(formatter) + + logger.addHandler(new_handler) + return logger + +######## Main Function ######## +def main(): + return + +def parse_args()-> Type[run_config]: + parser = argparse.ArgumentParser(description="Run various processor tests at the top level of RISCVBusisness") + parser.add_argument("--config_file", "-c", dest="config_file", + type=str, default=DEFAULT_CONFIG_FILE, + help="Specify the config file path") + parser.add_argument("--arch", "-a", dest="arch", + type=str, default=None, + help="Specify the architecture targeted. Option(s): RV32I Default: RV32I") + parser.add_argument("--test_type", "-t", dest="test_type", + type=str, default=None, + help="Specify what type of tests to run. Option(s): asm, selfasm,c Default: selfasm") + parser.add_argument("file_names", metavar="file_names", + type=str, nargs="*", + help="Run all tests that begin with this string. Optional") + parser.add_argument("--clean", action="store_true", dest="clean", + help="Clean the cache file before running") + args = parser.parse_args() + + conf_dict = dict() + with open(args.config_file, "r") as conf_fp: + conf_dict = json.load(conf_fp) + + config = run_config(conf_dict) + if(args.arch): + config.arch = args.arch + if(args.test_type): + config.test_type = args.test_type + if(args.file_names): + # find all the files that match the pattern + print(args.file_names) + config.test_filenames = args.file_names + # get the list of test files + test_files =[] + for filename in config.test_filenames: + test_files.append(glob.glob(str(config.test_dir/filename))) + config.test_filepaths = list(itertools.chain(*test_files)) + + # process the clean flag, remove the cache file if want a clean run + if(args.clean): + try: + os.remove(config.cache_file) + except FileNotFoundError: + pass # ignore if the file is not there + + + return config + +######## Main Function ######## +if __name__ == "__main__": + print("Running Main...") + # setup the logfile + #logging.basicConfig(filename=log_filepath, mode="w", level=logging.DEBUG) + config = parse_args() + run_tests(config) + # shutdown any remaining loggers + logging.shutdown() From d283c48a123b39be3e52cce83a2689cb482d2d73 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Thu, 6 Oct 2022 11:51:25 -0400 Subject: [PATCH 13/47] Fix for RV32C incorrect execution. Occasionally, when RV32C is able to provide an instruction early (i.e. buffered compressed instruction), execution could be skipped due to some validity flags relying on waiting for I-fetch. This provides a small fix to the hazard unit that detects the "early finish" condition and allows the updates the same as if instruction fetch had completed. --- source_code/include/tspp_hazard_unit_if.vh | 7 +++++-- source_code/pipelines/tspp/tspp_fetch_stage.sv | 2 ++ source_code/pipelines/tspp/tspp_hazard_unit.sv | 11 +++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/source_code/include/tspp_hazard_unit_if.vh b/source_code/include/tspp_hazard_unit_if.vh index 273bf0def..f7f9f3e7c 100644 --- a/source_code/include/tspp_hazard_unit_if.vh +++ b/source_code/include/tspp_hazard_unit_if.vh @@ -44,18 +44,21 @@ interface tspp_hazard_unit_if(); // Pipeline Tokens logic token_ex; + // RV32C + logic rv32c_ready; + modport hazard_unit ( input i_mem_busy, d_mem_busy, dren, dwen, jump, branch, mispredict, halt, pc,fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, ret, - epc_f, epc_e, badaddr_f, badaddr_e, token_ex, fence_stall, + epc_f, epc_e, badaddr_f, badaddr_e, token_ex, fence_stall, rv32c_ready, output pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren ); modport fetch ( input pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren, - output i_mem_busy, fault_insn, mal_insn, epc_f, badaddr_f + output i_mem_busy, fault_insn, mal_insn, epc_f, badaddr_f, rv32c_ready ); modport execute ( diff --git a/source_code/pipelines/tspp/tspp_fetch_stage.sv b/source_code/pipelines/tspp/tspp_fetch_stage.sv index 5a1cbdcc9..3d6c141e0 100644 --- a/source_code/pipelines/tspp/tspp_fetch_stage.sv +++ b/source_code/pipelines/tspp/tspp_fetch_stage.sv @@ -76,6 +76,8 @@ module tspp_fetch_stage ( assign igen_bus_if.wen = 1'b0; assign igen_bus_if.byte_en = 4'b1111; assign igen_bus_if.wdata = '0; + + assign hazard_if.rv32c_ready = rv32cif.done_earlier; //Fetch Execute Pipeline Signals word_t instr_to_ex; diff --git a/source_code/pipelines/tspp/tspp_hazard_unit.sv b/source_code/pipelines/tspp/tspp_hazard_unit.sv index 163841f04..d8546bb9c 100644 --- a/source_code/pipelines/tspp/tspp_hazard_unit.sv +++ b/source_code/pipelines/tspp/tspp_hazard_unit.sv @@ -57,7 +57,7 @@ module tspp_hazard_unit assign dmem_access = (hazard_if.dren || hazard_if.dwen); assign branch_jump = hazard_if.jump || (hazard_if.branch && hazard_if.mispredict); - assign wait_for_imem = hazard_if.iren & hazard_if.i_mem_busy; + assign wait_for_imem = hazard_if.iren & hazard_if.i_mem_busy & ~hazard_if.rv32c_ready; assign wait_for_dmem = dmem_access & hazard_if.d_mem_busy; assign hazard_if.npc_sel = branch_jump; @@ -69,11 +69,10 @@ module tspp_hazard_unit (wait_for_imem & dmem_access & ~hazard_if.d_mem_busy); - assign hazard_if.if_ex_stall = (wait_for_dmem || - (wait_for_imem & ~dmem_access) || - hazard_if.halt) & (~ex_flush_hazard | e_ex_stage) || - hazard_if.fence_stall || - rm_if.execute_stall; + assign hazard_if.if_ex_stall = (wait_for_dmem || (wait_for_imem & ~dmem_access) || hazard_if.halt) + & (~ex_flush_hazard | e_ex_stage) + || hazard_if.fence_stall + || rm_if.execute_stall; /* Hazards due to Interrupts/Exceptions */ assign prv_pipe_if.ret = hazard_if.ret; From 8e96b87e95da35a569c6acd0ff608bcd864dbce4 Mon Sep 17 00:00:00 2001 From: Hadi Ahmed Date: Wed, 12 Oct 2022 09:52:51 -0400 Subject: [PATCH 14/47] Privileged Unit 1.12 CSR Update (#17) Implementation of complete M-Mode Priv 1.12 spec. Co-authored-by: Hadi Ahmed Project17 SOC Co-authored-by: Cole Nelson --- .gitignore | 14 + Makefile | 4 +- isa_test.yml | 34 ++ run_tests_config.json | 4 +- run_tests_verilator.py | 0 source_code/include/control_unit_if.vh | 17 +- source_code/include/priv_1_12_internal_if.vh | 107 +++++ source_code/include/priv_ext_if.vh | 58 +++ source_code/include/prv_pipeline_if.vh | 22 +- .../packages/machine_mode_types_1_12_pkg.sv | 439 ++++++++++++++++++ source_code/packages/pmp_types_1_12_pkg.sv | 144 ++++++ .../pipelines/tspp/tspp_execute_stage.sv | 87 ++-- .../pipelines/tspp/tspp_fetch_stage.sv | 2 +- .../privs/priv_1_12/priv_1_12_block.sv | 112 +++++ source_code/privs/priv_1_12/priv_1_12_csr.sv | 392 ++++++++++++++++ .../priv_1_12/priv_1_12_int_ex_handler.sv | 211 +++++++++ .../privs/priv_1_12/priv_1_12_pipe_control.sv | 50 ++ source_code/privs/priv_wrapper.sv | 11 +- source_code/standard_core/control_unit.sv | 27 +- source_code/tb/tb_priv_1_12_block.sv | 303 ++++++++++++ tb_core.cc | 124 ++++- .../interrupts-exceptions/build_all.py | 51 ++ verification/interrupts-exceptions/ecall.c | 41 ++ verification/interrupts-exceptions/link.ld | 14 + verification/interrupts-exceptions/start.S | 17 + verification/interrupts-exceptions/utility.c | 25 + verification/interrupts-exceptions/utility.h | 19 + verification/interrupts-exceptions/vectored.c | 98 ++++ verification/self-tests/RV32I/timer.S | 75 --- 29 files changed, 2334 insertions(+), 168 deletions(-) create mode 100644 isa_test.yml mode change 100644 => 100755 run_tests_verilator.py create mode 100644 source_code/include/priv_1_12_internal_if.vh create mode 100644 source_code/include/priv_ext_if.vh create mode 100644 source_code/packages/machine_mode_types_1_12_pkg.sv create mode 100644 source_code/packages/pmp_types_1_12_pkg.sv create mode 100644 source_code/privs/priv_1_12/priv_1_12_block.sv create mode 100644 source_code/privs/priv_1_12/priv_1_12_csr.sv create mode 100644 source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv create mode 100644 source_code/privs/priv_1_12/priv_1_12_pipe_control.sv create mode 100644 source_code/tb/tb_priv_1_12_block.sv create mode 100755 verification/interrupts-exceptions/build_all.py create mode 100644 verification/interrupts-exceptions/ecall.c create mode 100755 verification/interrupts-exceptions/link.ld create mode 100644 verification/interrupts-exceptions/start.S create mode 100644 verification/interrupts-exceptions/utility.c create mode 100644 verification/interrupts-exceptions/utility.h create mode 100644 verification/interrupts-exceptions/vectored.c delete mode 100644 verification/self-tests/RV32I/timer.S diff --git a/.gitignore b/.gitignore index ea3f5ec84..0f714ce59 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ meminit.ver verification/asm-tests/RV32I/*.elf verification/asm-tests/RV32I/*.log verification/asm-tests/RV32I/*.hex +*.bin +*.elf # config files are auto-generated source_code/include/component_selection_defines.vh verification/c-firmware/custom_instruction_calls.h @@ -46,4 +48,16 @@ _vmake *.qtl *.qpg +# ignore python bytecode files +*.pyc +__pycache__/ + +# ignore xrun files +INCA_libs/ +xcelium.d/ +xrun.history +xrun.key +.simvision +waves.shm/ + open_files.sh diff --git a/Makefile b/Makefile index 7dd22f857..b28b56e20 100644 --- a/Makefile +++ b/Makefile @@ -15,11 +15,11 @@ RV32M_FILES := $(RISC_MGMT)/extensions/rv32m/carry_save_adder.sv $(RISC_MGMT)/ex RV32C_FILES := $(RV32C)/decompressor.sv $(RV32C)/fetch_buffer.sv $(RV32C)/rv32c_disabled.sv $(RV32C)/rv32c_enabled.sv $(RV32C)/rv32c_wrapper.sv RISC_MGMT_FILES := $(RISC_MGMT)/risc_mgmt_wrapper.sv $(RISC_MGMT)/tspp/tspp_risc_mgmt.sv $(RV32M_FILES) RISC_EXT_FILES := $(RISC_MGMT)/extensions/template/template_decode.sv $(RISC_MGMT)/extensions/template/template_execute.sv $(RISC_MGMT)/extensions/template/template_memory.sv -CORE_PKG_FILES := $(RISCV_PKGS)/rv32i_types_pkg.sv $(RISCV_PKGS)/alu_types_pkg.sv $(RISCV_PKGS)/risc_mgmt/template_pkg.sv $(RISCV_PKGS)/risc_mgmt/crc32_pkg.sv $(RISCV_PKGS)/risc_mgmt/rv32m_pkg.sv $(RISCV_PKGS)/risc_mgmt/test_pkg.sv $(RISCV_PKGS)/machine_mode_types_1_11_pkg.sv $(RISCV_PKGS)/machine_mode_types_pkg.sv +CORE_PKG_FILES := $(RISCV_PKGS)/rv32i_types_pkg.sv $(RISCV_PKGS)/alu_types_pkg.sv $(RISCV_PKGS)/risc_mgmt/template_pkg.sv $(RISCV_PKGS)/risc_mgmt/crc32_pkg.sv $(RISCV_PKGS)/risc_mgmt/rv32m_pkg.sv $(RISCV_PKGS)/risc_mgmt/test_pkg.sv $(RISCV_PKGS)/machine_mode_types_1_12_pkg.sv $(RISCV_PKGS)/machine_mode_types_pkg.sv CORE_FILES := $(RISCV_CORE)/alu.sv $(RISCV_CORE)/branch_res.sv $(RISCV_CORE)/control_unit.sv $(RISCV_CORE)/dmem_extender.sv $(RISCV_CORE)/endian_swapper.sv $(RISCV_CORE)/jump_calc.sv $(RISCV_CORE)/memory_controller.sv $(RISCV_CORE)/RISCVBusiness.sv $(RISCV_CORE)/rv32i_reg_file.sv $(RISCV_CORE)/top_core.sv PIPELINE_FILES := $(PIPELINE)/tspp/tspp_execute_stage.sv $(PIPELINE)/tspp/tspp_fetch_stage.sv $(PIPELINE)/tspp/tspp_hazard_unit.sv #$(PIPELINE)/tspp/tspp.sv PREDICTOR_FILES := $(BRANCH_PREDICT)/branch_predictor_wrapper.sv $(BRANCH_PREDICT)/nottaken_predictor/nottaken_predictor.sv -PRIV_FILES := $(PRIVS)/priv_wrapper.sv $(PRIVS)/priv_1_11/priv_1_11_block.sv $(PRIVS)/priv_1_11/priv_1_11_control.sv $(PRIVS)/priv_1_11/priv_1_11_csr_rfile.sv $(PRIVS)/priv_1_11/priv_1_11_pipeline_control.sv +PRIV_FILES := $(PRIVS)/priv_wrapper.sv $(PRIVS)/priv_1_12/priv_1_12_block.sv $(PRIVS)/priv_1_12/priv_1_12_int_ex_handler.sv $(PRIVS)/priv_1_12/priv_1_12_csr.sv $(PRIVS)/priv_1_12/priv_1_12_pipe_control.sv CACHE_FILES := $(CACHES)/caches_wrapper.sv $(CACHES)/pass_through/pass_through_cache.sv $(CACHES)/direct_mapped_tpf/direct_mapped_tpf_cache.sv $(CACHES)/separate_caches.sv SPARCE_FILES := $(SPARCE)/sparce_wrapper.sv $(SPARCE)/sparce_disabled/sparce_disabled.sv $(SPARCE)/sparce_enabled/sparce_cfid.sv $(SPARCE)/sparce_enabled/sparce_enabled.sv $(SPARCE)/sparce_enabled/sparce_psru.sv $(SPARCE)/sparce_enabled/sparce_sasa_table.sv $(SPARCE)/sparce_enabled/sparce_sprf.sv $(SPARCE)/sparce_enabled/sparce_svc.sv RISCV_BUS_FILES := $(RISCV_BUS)/generic_nonpipeline.sv #$(RISCV_BUS)/ahb.sv diff --git a/isa_test.yml b/isa_test.yml new file mode 100644 index 000000000..e3a9ea0c7 --- /dev/null +++ b/isa_test.yml @@ -0,0 +1,34 @@ +# ISA Configurations +isa_params: + xlen : 32 + +# Microarchitectural Configurations +microarch_params: + # Branch/Jump Configurations + br_predictor_type : "not_taken" + + # Cache configurations + cache_config : "separate" + dcache_type : "pass_through" + icache_type : "pass_through" + + # Bus configurations + bus_endianness : "big" + bus_interface_type : "generic_bus_if" + + # Sparisty Optimizations + sparce_enabled : "disabled" + + # RV32C + rv32c_enabled : "disabled" + + # Halt + infinite_loop_halts : "true" + + # base ISA + base_isa: "RV32I" + +# RISC-MGMT Extension Configuration +risc_mgmt_params: + standard_extensions: + nonstandard_extensions: diff --git a/run_tests_config.json b/run_tests_config.json index 93a909fd8..df4872c65 100644 --- a/run_tests_config.json +++ b/run_tests_config.json @@ -1,11 +1,11 @@ { "arch": "RV32I", "abi": "ilp32", - "xlen": "rv32imc", + "xlen": "rv32i", "test_type": "self-tests", "top_level": "RISCVBusiness_self_test", "test_filenames": ["*.S"], - + "cache_file": "run_tests_cache.json", "build_dir": "./build", "verif_dir": "./verification", diff --git a/run_tests_verilator.py b/run_tests_verilator.py old mode 100644 new mode 100755 diff --git a/source_code/include/control_unit_if.vh b/source_code/include/control_unit_if.vh index 29049f6e6..edb448b6e 100644 --- a/source_code/include/control_unit_if.vh +++ b/source_code/include/control_unit_if.vh @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,7 +29,7 @@ interface control_unit_if; import alu_types_pkg::*; import rv32i_types_pkg::*; - import machine_mode_types_1_11_pkg::*; + import machine_mode_types_1_12_pkg::*; logic dwen, dren, j_sel, branch, jump, ex_pc_sel, imm_shamt_sel, halt, wen, ifence, wfi; aluop_t alu_op; @@ -42,7 +42,7 @@ interface control_unit_if; word_t instr, imm_U; load_t load_type; branch_t branch_type; - opcode_t opcode; + opcode_t opcode; // Privilege control signals logic fault_insn, illegal_insn, ret_insn, breakpoint, ecall_insn; @@ -51,15 +51,14 @@ interface control_unit_if; logic [4:0] zimm; modport control_unit( - input instr, + input instr, output dwen, dren, j_sel, branch, jump, ex_pc_sel, alu_a_sel, alu_b_sel, w_sel, load_type, branch_type, shamt, - imm_I, imm_S, imm_SB, imm_UJ, imm_U, imm_shamt_sel, alu_op, - opcode, halt, wen, fault_insn, illegal_insn, ret_insn, breakpoint, + imm_I, imm_S, imm_SB, imm_UJ, imm_U, imm_shamt_sel, alu_op, + opcode, halt, wen, fault_insn, illegal_insn, ret_insn, breakpoint, ecall_insn, csr_swap, csr_set, csr_clr, csr_imm, csr_rw_valid, csr_addr, zimm, ifence, wfi ); endinterface `endif - diff --git a/source_code/include/priv_1_12_internal_if.vh b/source_code/include/priv_1_12_internal_if.vh new file mode 100644 index 000000000..4542efeb7 --- /dev/null +++ b/source_code/include/priv_1_12_internal_if.vh @@ -0,0 +1,107 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_internal_if.vh +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/27/2022 +* Description: Interface for components within the privilege block v1.12 +*/ + +`ifndef PRIV_1_12_INTERNAL_IF_VH +`define PRIV_1_12_INTERNAL_IF_VH + +`include "component_selection_defines.vh" + +interface priv_1_12_internal_if; + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + // RISC-MGMT? + // not sure what these are for, part of priv 1.11 + logic ex_rmgmt; + logic [$clog2(`NUM_EXTENSIONS)-1:0] ex_rmgmt_cause; + + // CSR block values + csr_addr_t csr_addr; // CSR address to read + priv_level_t curr_priv; // Current process privilege + logic csr_write, csr_set, csr_clear; // Is the CSR currently being modified? + logic invalid_csr; // Bad CSR address + logic inst_ret; // signal when an instruction is retired + word_t new_csr_val, old_csr_val; // new and old CSR values (atomically swapped) + logic valid_write; // valid write occurs with an r type instruction that does not have any pipeline stalls + + // Sources for interrupts + logic timer_int_u, timer_int_s, timer_int_m; + logic soft_int_u, soft_int_s, soft_int_m; + logic ext_int_u, ext_int_s, ext_int_m; + + // Sources to clear the pending interrupt + logic clear_timer_int_u, clear_timer_int_s, clear_timer_int_m; + logic clear_soft_int_u, clear_soft_int_s, clear_soft_int_m; + logic clear_ext_int_u, clear_ext_int_s, clear_ext_int_m; + + // Sources for exceptions + logic mal_insn, fault_insn_access, illegal_insn, breakpoint, fault_l, mal_l, fault_s, mal_s; + logic env_u, env_s, env_m, fault_insn_page, fault_load_page, fault_store_page; + + // Values involving CSR file for ex/int handling + mip_t curr_mip, next_mip; + mie_t curr_mie, next_mie; + mcause_t curr_mcause, next_mcause; + csr_reg_t curr_mepc, next_mepc; + mstatus_t curr_mstatus, next_mstatus; + mtvec_t curr_mtvec; + csr_reg_t curr_mtval, next_mtval; + logic inject_mip, inject_mie, inject_mcause, inject_mepc, inject_mstatus, inject_mtval; + + // Things from the pipe we care about + word_t epc; // pc of the instruction prior to the exception + word_t priv_pc; // pc to handle the interrupt/exception + logic pipe_clear; // is the pipeline clear of hazards + logic insert_pc; // inform pipeline that we are changing the PC + logic mret, sret, uret; // returning from a trap instruction + logic intr; // Did something trigger an interrupt? + + + + modport csr ( + input csr_addr, curr_priv, csr_write, csr_set, csr_clear, new_csr_val, inst_ret, valid_write, + inject_mcause, inject_mepc, inject_mie, inject_mip, inject_mstatus, inject_mtval, + next_mcause, next_mepc, next_mie, next_mip, next_mstatus, next_mtval, + output old_csr_val, invalid_csr, + curr_mcause, curr_mepc, curr_mie, curr_mip, curr_mstatus, curr_mtval, curr_mtvec + ); + + modport int_ex_handler ( + input timer_int_u, timer_int_s, timer_int_m, soft_int_u, soft_int_s, soft_int_m, ext_int_u, ext_int_s, ext_int_m, + clear_timer_int_u, clear_timer_int_s, clear_timer_int_m, clear_soft_int_u, clear_soft_int_s, clear_soft_int_m, + clear_ext_int_u, clear_ext_int_s, clear_ext_int_m, mal_insn, fault_insn_access, illegal_insn, breakpoint, fault_l, mal_l, fault_s, mal_s, + env_u, env_s, env_m, fault_insn_page, fault_load_page, fault_store_page, curr_mcause, curr_mepc, curr_mie, curr_mip, curr_mstatus, curr_mtval, + mret, sret, uret, pipe_clear, ex_rmgmt, ex_rmgmt_cause, epc, + output inject_mcause, inject_mepc, inject_mie, inject_mip, inject_mstatus, inject_mtval, + next_mcause, next_mepc, next_mie, next_mip, next_mstatus, next_mtval, intr + ); + + modport pipe_ctrl ( + input intr, pipe_clear, mret, sret, uret, curr_mtvec, curr_mcause, curr_mepc, + output insert_pc, priv_pc + ); + +endinterface + +`endif // PRIV_1_12_INTERNAL_IF_VH diff --git a/source_code/include/priv_ext_if.vh b/source_code/include/priv_ext_if.vh new file mode 100644 index 000000000..836b7e63a --- /dev/null +++ b/source_code/include/priv_ext_if.vh @@ -0,0 +1,58 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: prv_ext_if.vh +* +* Created by: Hadi AHmed +* Email: ahmed138@purdue.edu +* Date Created: 10/36/2022 +* Description: Interface connecting the main CSR file to other extension + CSR files so they can handle their state independently. +*/ + +`ifndef PRIV_EXT_IF_VH +`define PRIV_EXT_IF_VH + +interface priv_ext_if(); +import machine_mode_types_1_12_pkg::*; +import rv32i_types_pkg::*; + +// from Priv-CSR to Ext-CSR +csr_addr_t csr_addr; // CSR address +word_t value_in; // New CSR value +logic csr_active; // active CSR operation + +// from Ext-CSR to Priv-CSR +logic invalid_csr, ack; // invalid_csr: error signal when processing CSR, ack: csr_addr belongs to extension +word_t value_out; // Old CSR value + +// Extensions must ALWAYS make sure they drive 'ack' high if 'csr_addr' belongs to them and that +// 'value_out' is the value of the CSR at 'csr_addr'. Only perform a write on 'csr_active', but +// the privileged unit will need the other values to calculate 'value_in'. + +modport priv ( + input invalid_csr, ack, value_out, + output csr_addr, value_in, csr_active +); + +modport ext ( + output invalid_csr, ack, value_out, + input csr_addr, value_in, csr_active +); + +endinterface + +`endif // PRIV_EXT_IF_VH \ No newline at end of file diff --git a/source_code/include/prv_pipeline_if.vh b/source_code/include/prv_pipeline_if.vh index 40c0bfda5..57ccf0775 100644 --- a/source_code/include/prv_pipeline_if.vh +++ b/source_code/include/prv_pipeline_if.vh @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,7 +20,7 @@ * Email: jskubic@purdue.edu * Date Created: 08/24/2016 * Description: Interface connecting the priv block to the pipeline. -* Contains connections between modules inside the priv block. +* Contains connections between modules inside the priv block. * TODO: These two functionalities should be split into two * separate interfaces. */ @@ -31,7 +31,7 @@ `include "component_selection_defines.vh" interface prv_pipeline_if(); - import machine_mode_types_1_11_pkg::*; + import machine_mode_types_1_12_pkg::*; import rv32i_types_pkg::*; // exception signals @@ -49,26 +49,26 @@ interface prv_pipeline_if(); // csr rw logic swap, clr, set; logic invalid_csr, valid_write; - csr_addr_t addr; + csr_addr_t csr_addr; word_t rdata, wdata; // performance signals logic wb_enable, instr; - // RISC-MGMT + // RISC-MGMT logic ex_rmgmt; logic [$clog2(`NUM_EXTENSIONS)-1:0] ex_rmgmt_cause; modport hazard ( input priv_pc, insert_pc, intr, - output pipe_clear, ret, epc, fault_insn, mal_insn, + output pipe_clear, ret, epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m, badaddr, wb_enable, + breakpoint, env_m, badaddr, wb_enable, ex_rmgmt, ex_rmgmt_cause ); modport pipe ( - output swap, clr, set, wdata, addr, valid_write, instr, + output swap, clr, set, wdata, csr_addr, valid_write, instr, input rdata, invalid_csr ); @@ -77,7 +77,7 @@ interface prv_pipeline_if(); input pipe_clear, ret, epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, badaddr, swap, clr, set, - wdata, addr, valid_write, wb_enable, instr, + wdata, csr_addr, valid_write, wb_enable, instr, ex_rmgmt, ex_rmgmt_cause, output priv_pc, insert_pc, intr, rdata, invalid_csr ); diff --git a/source_code/packages/machine_mode_types_1_12_pkg.sv b/source_code/packages/machine_mode_types_1_12_pkg.sv new file mode 100644 index 000000000..565544389 --- /dev/null +++ b/source_code/packages/machine_mode_types_1_12_pkg.sv @@ -0,0 +1,439 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: machine_mode_types_1_12_pkg.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/08/2022 +* Description: Types needed to implement machine mode priv. isa 1.12 +*/ + +`ifndef MACHINE_MODE_TYPES_1_12_PKG_SV +`define MACHINE_MODE_TYPES_1_12_PKG_SV + +package machine_mode_types_1_12_pkg; + + /* Machine Mode Addresses */ + typedef enum logic [11:0] { + /* Machine Information Registers */ + MVENDORID_ADDR = 12'hF11, + MARCHID_ADDR = 12'hF12, + MIMPID_ADDR = 12'hF13, + MHARTID_ADDR = 12'hF14, + MCONFIGPTR_ADDR = 12'hF15, + + /* Machine Trap Setup */ + MSTATUS_ADDR = 12'h300, + MISA_ADDR = 12'h301, + MEDELEG_ADDR = 12'h302, + MIDELEG_ADDR = 12'h303, + MIE_ADDR = 12'h304, + MTVEC_ADDR = 12'h305, + MCOUNTEREN_ADDR = 12'h306, + MSTATUSH_ADDR = 12'h310, + + /* Machine Trap Handling */ + MSCRATCH_ADDR = 12'h340, + MEPC_ADDR = 12'h341, + MCAUSE_ADDR = 12'h342, + MTVAL_ADDR = 12'h343, + MIP_ADDR = 12'h344, + MTINST_ADDR = 12'h34A, + MTVAL2_ADDR = 12'h34B, + + /* Machine Configuration */ + MENVCFG_ADDR = 12'h30A, + MENVCFGH_ADDR = 12'h31A, + MSECCFG_ADDR = 12'h747, + MSECCFGH_ADDR = 12'h757, + + /* Machine Memory Protection */ + /* See pmp_types_1_12_pkg.sv */ + + /* Machine Counter/Timers */ + MCYCLE_ADDR = 12'hB00, + MINSTRET_ADDR = 12'hB02, + MHPMCOUNTER3_ADDR = 12'hB03, + MHPMCOUNTER4_ADDR = 12'hB04, + MHPMCOUNTER5_ADDR = 12'hB05, + MHPMCOUNTER6_ADDR = 12'hB06, + MHPMCOUNTER7_ADDR = 12'hB07, + MHPMCOUNTER8_ADDR = 12'hB08, + MHPMCOUNTER9_ADDR = 12'hB09, + MHPMCOUNTER10_ADDR = 12'hB0A, + MHPMCOUNTER11_ADDR = 12'hB0B, + MHPMCOUNTER12_ADDR = 12'hB0C, + MHPMCOUNTER13_ADDR = 12'hB0D, + MHPMCOUNTER14_ADDR = 12'hB0E, + MHPMCOUNTER15_ADDR = 12'hB0F, + MHPMCOUNTER16_ADDR = 12'hB10, + MHPMCOUNTER17_ADDR = 12'hB11, + MHPMCOUNTER18_ADDR = 12'hB12, + MHPMCOUNTER19_ADDR = 12'hB13, + MHPMCOUNTER20_ADDR = 12'hB14, + MHPMCOUNTER21_ADDR = 12'hB15, + MHPMCOUNTER22_ADDR = 12'hB16, + MHPMCOUNTER23_ADDR = 12'hB17, + MHPMCOUNTER24_ADDR = 12'hB18, + MHPMCOUNTER25_ADDR = 12'hB19, + MHPMCOUNTER26_ADDR = 12'hB1A, + MHPMCOUNTER27_ADDR = 12'hB1B, + MHPMCOUNTER28_ADDR = 12'hB1C, + MHPMCOUNTER29_ADDR = 12'hB1D, + MHPMCOUNTER30_ADDR = 12'hB1E, + MHPMCOUNTER31_ADDR = 12'hB1F, + MCYCLEH_ADDR = 12'hB80, + MINSTRETH_ADDR = 12'hB82, + MHPMCOUNTER3H_ADDR = 12'hB83, + MHPMCOUNTER4H_ADDR = 12'hB84, + MHPMCOUNTER5H_ADDR = 12'hB85, + MHPMCOUNTER6H_ADDR = 12'hB86, + MHPMCOUNTER7H_ADDR = 12'hB87, + MHPMCOUNTER8H_ADDR = 12'hB88, + MHPMCOUNTER9H_ADDR = 12'hB89, + MHPMCOUNTER10H_ADDR = 12'hB8A, + MHPMCOUNTER11H_ADDR = 12'hB8B, + MHPMCOUNTER12H_ADDR = 12'hB8C, + MHPMCOUNTER13H_ADDR = 12'hB8D, + MHPMCOUNTER14H_ADDR = 12'hB8E, + MHPMCOUNTER15H_ADDR = 12'hB8F, + MHPMCOUNTER16H_ADDR = 12'hB90, + MHPMCOUNTER17H_ADDR = 12'hB91, + MHPMCOUNTER18H_ADDR = 12'hB92, + MHPMCOUNTER19H_ADDR = 12'hB93, + MHPMCOUNTER20H_ADDR = 12'hB94, + MHPMCOUNTER21H_ADDR = 12'hB95, + MHPMCOUNTER22H_ADDR = 12'hB96, + MHPMCOUNTER23H_ADDR = 12'hB97, + MHPMCOUNTER24H_ADDR = 12'hB98, + MHPMCOUNTER25H_ADDR = 12'hB99, + MHPMCOUNTER26H_ADDR = 12'hB9A, + MHPMCOUNTER27H_ADDR = 12'hB9B, + MHPMCOUNTER28H_ADDR = 12'hB9C, + MHPMCOUNTER29H_ADDR = 12'hB9D, + MHPMCOUNTER30H_ADDR = 12'hB9E, + MHPMCOUNTER31H_ADDR = 12'hB9F, + + /* Machine Counter Setup */ + MCOUNTINHIBIT_ADDR = 12'h320, + MHPMEVENT3_ADDR = 12'h323, + MHPMEVENT4_ADDR = 12'h324, + MHPMEVENT5_ADDR = 12'h325, + MHPMEVENT6_ADDR = 12'h326, + MHPMEVENT7_ADDR = 12'h327, + MHPMEVENT8_ADDR = 12'h328, + MHPMEVENT9_ADDR = 12'h329, + MHPMEVENT10_ADDR = 12'h32A, + MHPMEVENT11_ADDR = 12'h32B, + MHPMEVENT12_ADDR = 12'h32C, + MHPMEVENT13_ADDR = 12'h32D, + MHPMEVENT14_ADDR = 12'h32E, + MHPMEVENT15_ADDR = 12'h32F, + MHPMEVENT16_ADDR = 12'h330, + MHPMEVENT17_ADDR = 12'h331, + MHPMEVENT18_ADDR = 12'h332, + MHPMEVENT19_ADDR = 12'h333, + MHPMEVENT20_ADDR = 12'h334, + MHPMEVENT21_ADDR = 12'h335, + MHPMEVENT22_ADDR = 12'h336, + MHPMEVENT23_ADDR = 12'h337, + MHPMEVENT24_ADDR = 12'h338, + MHPMEVENT25_ADDR = 12'h339, + MHPMEVENT26_ADDR = 12'h33A, + MHPMEVENT27_ADDR = 12'h33B, + MHPMEVENT28_ADDR = 12'h33C, + MHPMEVENT29_ADDR = 12'h33D, + MHPMEVENT30_ADDR = 12'h33E, + MHPMEVENT31_ADDR = 12'h33F + } maddr_t; + + + /* Machine Mode Register Types */ + + /* misa types */ + + typedef enum logic [1:0] { + BASE_RV32 = 2'h1, + BASE_RV64 = 2'h2, + BASE_RV128 = 2'h3 + } misa_base_t; + + typedef struct packed { + misa_base_t base; + logic [3:0] zero; + logic [25:0] extensions; + } misa_t; + + parameter MISA_EXT_A = 26'h1 << 0; + parameter MISA_EXT_B = 26'h1 << 1; + parameter MISA_EXT_C = 26'h1 << 2; + parameter MISA_EXT_D = 26'h1 << 3; + parameter MISA_EXT_E = 26'h1 << 4; + parameter MISA_EXT_F = 26'h1 << 5; + parameter MISA_EXT_G = 26'h1 << 6; + parameter MISA_EXT_H = 26'h1 << 7; + parameter MISA_EXT_I = 26'h1 << 8; + parameter MISA_EXT_J = 26'h1 << 9; + parameter MISA_EXT_K = 26'h1 << 10; + parameter MISA_EXT_L = 26'h1 << 11; + parameter MISA_EXT_M = 26'h1 << 12; + parameter MISA_EXT_N = 26'h1 << 13; + parameter MISA_EXT_O = 26'h1 << 14; + parameter MISA_EXT_P = 26'h1 << 15; + parameter MISA_EXT_Q = 26'h1 << 16; + parameter MISA_EXT_R = 26'h1 << 17; + parameter MISA_EXT_S = 26'h1 << 18; + parameter MISA_EXT_T = 26'h1 << 19; + parameter MISA_EXT_U = 26'h1 << 20; + parameter MISA_EXT_V = 26'h1 << 21; + parameter MISA_EXT_W = 26'h1 << 22; + parameter MISA_EXT_X = 26'h1 << 23; + parameter MISA_EXT_Y = 26'h1 << 24; + parameter MISA_EXT_Z = 26'h1 << 25; + + /* mstatus types */ + + typedef enum logic [1:0] { + FS_OFF = 2'h0, + FS_INITIAL = 2'h1, + FS_CLEAN = 2'h2, + FS_DIRTY = 2'h3 + } fs_t; + + typedef enum logic [1:0] { + VS_OFF = 2'h0, + VS_INITIAL = 2'h1, + VS_CLEAN = 2'h2, + VS_DIRTY = 2'h3 + } vs_t; + + typedef enum logic [1:0] { + XS_ALL_OFF = 2'h0, + XS_NONE_DC = 2'h1, + XS_NONE_D = 2'h2, + XS_SOME_D = 2'h3 + } xs_t; + + typedef enum logic [1:0] { + U_MODE = 2'h0, + S_MODE = 2'h1, + RESERVED_MODE = 2'h2, + M_MODE = 2'h3 + } priv_level_t; + + typedef struct packed { + logic sd; // Extension dirty signal (R/O) + logic [7:0] reserved_3; + logic tsr; // Trap SRET (no effect without S-Mode) + logic tw; // Timeout wait + logic tvm; // Trap virtual memory (no effect without S-Mode) + logic mxr; // Make executable readable (no effect without S-Mode) + logic sum; // Supervisor user memory access (no effect without S-Mode) + logic mprv; // Modify privilege + xs_t xs; // User extensions state + fs_t fs; // FP extension state + priv_level_t mpp; // Previous privilege level + vs_t vs; // Vector extension state + logic spp; // Supervisor previous privilege level (no effect without S-Mode) + logic mpie; // M-Mode previous enable + logic ube; // U-Mode endianness control + logic spie; // S-Mode previous enable + logic reserved_2; + logic mie; // M-Mode interrupt enable + logic reserved_1; + logic sie; // S-Mode interrupt enable + logic reserved_0; + } mstatus_t; + + typedef struct packed { + logic [25:0] reserved_1; + logic mbe; // M-Mode endianness control + logic sbe; // S-Mode endianness control + logic [3:0] reserved_0; + } mstatush_t; + + /* mtvec types */ + typedef enum logic [1:0] { + DIRECT = 2'h0, + VECTORED = 2'h1, + RES_0 = 2'h2, + RES_1 = 2'h3 + } vector_modes_t; + + typedef struct packed { + logic [29:0] base; + vector_modes_t mode; + } mtvec_t; + + /* mip and mie types */ + + // Decreasing priority: MEI, MSI, MTI + + typedef struct packed { + logic [15:0] impl_defined; // Implementation defined + logic [3:0] zero_6; + logic meip; // M-Mode external interrupt + logic zero_5; + logic seip; // S-Mode external interrupt + logic zero_4; + logic mtip; // M-Mode timer interrupt + logic zero_3; + logic stip; // S-Mode timer interrupt + logic zero_2; + logic msip; // M-Mode software interrupt + logic zero_1; + logic ssip; // S-Mode software interrupt + logic zero_0; + } mip_t; + + typedef struct packed { + logic [15:0] impl_defined; + logic [3:0] zero_6; + logic meie; + logic zero_5; + logic seie; + logic zero_4; + logic mtie; + logic zero_3; + logic stie; + logic zero_2; + logic msie; + logic zero_1; + logic ssie; + logic zero_0; + } mie_t; + + /* mcounteren and mcountinhibit types */ + typedef struct packed { + logic hpm31; + logic hpm30; + logic hpm29; + logic hpm28; + logic hpm27; + logic hpm26; + logic hpm25; + logic hpm24; + logic hpm23; + logic hpm22; + logic hpm21; + logic hpm20; + logic hpm19; + logic hpm18; + logic hpm17; + logic hpm16; + logic hpm15; + logic hpm14; + logic hpm13; + logic hpm12; + logic hpm11; + logic hpm10; + logic hpm9; + logic hpm8; + logic hpm7; + logic hpm6; + logic hpm5; + logic hpm4; + logic hpm3; + logic ir; + logic tm; + logic cy; + } mcounteren_t; + + typedef struct packed { + logic hpm31; + logic hpm30; + logic hpm29; + logic hpm28; + logic hpm27; + logic hpm26; + logic hpm25; + logic hpm24; + logic hpm23; + logic hpm22; + logic hpm21; + logic hpm20; + logic hpm19; + logic hpm18; + logic hpm17; + logic hpm16; + logic hpm15; + logic hpm14; + logic hpm13; + logic hpm12; + logic hpm11; + logic hpm10; + logic hpm9; + logic hpm8; + logic hpm7; + logic hpm6; + logic hpm5; + logic hpm4; + logic hpm3; + logic ir; + logic reserved_0; + logic cy; + } mcountinhibit_t; + + /* mcause register variables */ + + typedef struct packed { + logic interrupt; + logic [30:0] cause; + } mcause_t; + + // ex_code_t should be cast from an + // instantiation of mcause_t + typedef enum logic [30:0] { + INSN_MAL = 31'd0, + INSN_ACCESS = 31'd1, + ILLEGAL_INSN = 31'd2, + BREAKPOINT = 31'd3, + L_ADDR_MAL = 31'd4, + L_FAULT = 31'd5, + S_ADDR_MAL = 31'd6, + S_FAULT = 31'd7, + ENV_CALL_U = 31'd8, + ENV_CALL_S = 31'd9, + ENV_CALL_M = 31'd11, + INSN_PAGE = 31'd12, + LOAD_PAGE = 31'd13, + STORE_PAGE = 31'd15 + } ex_code_t; + + typedef enum logic [30:0] { + SOFT_INT_S = 31'd1, + SOFT_INT_M = 31'd3, + TIMER_INT_S = 31'd5, + TIMER_INT_M = 31'd7, + EXT_INT_S = 31'd9, + EXT_INT_M = 31'd11 + } int_code_t; + + // General CSR definition + typedef logic [11:0] csr_addr_t; + typedef logic [31:0] csr_reg_t; + typedef logic [63:0] long_csr_t; + + //Non Standard Extentions + // Unsure what these are for, + // part of priv 1.11 + typedef logic [31:0] mtohost_t; + typedef logic [31:0] mfromhost_t; + +endpackage + +`endif //MACHINE_MODE_TYPES_1_12_PKG_SV diff --git a/source_code/packages/pmp_types_1_12_pkg.sv b/source_code/packages/pmp_types_1_12_pkg.sv new file mode 100644 index 000000000..64a48cc8a --- /dev/null +++ b/source_code/packages/pmp_types_1_12_pkg.sv @@ -0,0 +1,144 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: pmp_types_1_12_pkg.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/20/2022 +* Description: Types needed to implement physical memory protection +*/ + +`ifndef PMP_TYPES_1_12_PKG_SV +`define PMP_TYPES_1_12_PKG_SV + +package pmp_types_1_12_pkg; + + /* PMP CSR Addresses */ + typedef enum logic [11:0] { + /* Machine Memory Protection */ + PMPCFG0_ADDR = 12'h3A0, + PMPCFG1_ADDR = 12'h3A1, + PMPCFG2_ADDR = 12'h3A2, + PMPCFG3_ADDR = 12'h3A3, + PMPCFG4_ADDR = 12'h3A4, + PMPCFG5_ADDR = 12'h3A5, + PMPCFG6_ADDR = 12'h3A6, + PMPCFG7_ADDR = 12'h3A7, + PMPCFG8_ADDR = 12'h3A8, + PMPCFG9_ADDR = 12'h3A9, + PMPCFG10_ADDR = 12'h3AA, + PMPCFG11_ADDR = 12'h3AB, + PMPCFG12_ADDR = 12'h3AC, + PMPCFG13_ADDR = 12'h3AD, + PMPCFG14_ADDR = 12'h3AE, + PMPCFG15_ADDR = 12'h3AF, + PMPADDR0_ADDR = 12'h3B0, + PMPADDR1_ADDR = 12'h3B1, + PMPADDR2_ADDR = 12'h3B2, + PMPADDR3_ADDR = 12'h3B3, + PMPADDR4_ADDR = 12'h3B4, + PMPADDR5_ADDR = 12'h3B5, + PMPADDR6_ADDR = 12'h3B6, + PMPADDR7_ADDR = 12'h3B7, + PMPADDR8_ADDR = 12'h3B8, + PMPADDR9_ADDR = 12'h3B9, + PMPADDR10_ADDR = 12'h3BA, + PMPADDR11_ADDR = 12'h3BB, + PMPADDR12_ADDR = 12'h3BC, + PMPADDR13_ADDR = 12'h3BD, + PMPADDR14_ADDR = 12'h3BE, + PMPADDR15_ADDR = 12'h3BF, + PMPADDR16_ADDR = 12'h3C0, + PMPADDR17_ADDR = 12'h3C1, + PMPADDR18_ADDR = 12'h3C2, + PMPADDR19_ADDR = 12'h3C3, + PMPADDR20_ADDR = 12'h3C4, + PMPADDR21_ADDR = 12'h3C5, + PMPADDR22_ADDR = 12'h3C6, + PMPADDR23_ADDR = 12'h3C7, + PMPADDR24_ADDR = 12'h3C8, + PMPADDR25_ADDR = 12'h3C9, + PMPADDR26_ADDR = 12'h3CA, + PMPADDR27_ADDR = 12'h3CB, + PMPADDR28_ADDR = 12'h3CC, + PMPADDR29_ADDR = 12'h3CD, + PMPADDR30_ADDR = 12'h3CE, + PMPADDR31_ADDR = 12'h3CF, + PMPADDR32_ADDR = 12'h3D0, + PMPADDR33_ADDR = 12'h3D1, + PMPADDR34_ADDR = 12'h3D2, + PMPADDR35_ADDR = 12'h3D3, + PMPADDR36_ADDR = 12'h3D4, + PMPADDR37_ADDR = 12'h3D5, + PMPADDR38_ADDR = 12'h3D6, + PMPADDR39_ADDR = 12'h3D7, + PMPADDR40_ADDR = 12'h3D8, + PMPADDR41_ADDR = 12'h3D9, + PMPADDR42_ADDR = 12'h3DA, + PMPADDR43_ADDR = 12'h3DB, + PMPADDR44_ADDR = 12'h3DC, + PMPADDR45_ADDR = 12'h3DD, + PMPADDR46_ADDR = 12'h3DE, + PMPADDR47_ADDR = 12'h3DF, + PMPADDR48_ADDR = 12'h3E0, + PMPADDR49_ADDR = 12'h3E1, + PMPADDR50_ADDR = 12'h3E2, + PMPADDR51_ADDR = 12'h3E3, + PMPADDR52_ADDR = 12'h3E4, + PMPADDR53_ADDR = 12'h3E5, + PMPADDR54_ADDR = 12'h3E6, + PMPADDR55_ADDR = 12'h3E7, + PMPADDR56_ADDR = 12'h3E8, + PMPADDR57_ADDR = 12'h3E9, + PMPADDR58_ADDR = 12'h3EA, + PMPADDR59_ADDR = 12'h3EB, + PMPADDR60_ADDR = 12'h3EC, + PMPADDR61_ADDR = 12'h3ED, + PMPADDR62_ADDR = 12'h3EE, + PMPADDR63_ADDR = 12'h3EF + } pmp_addr_t; + + /* pmpcfg types */ + + typedef enum logic [1:0] { + OFF = 2'b00, + TOR = 2'b01, + NA4 = 2'b10, + NAPOT = 2'b11 + } pmp_mode_t; + + typedef struct packed { + logic L; + logic [1:0] reserved; + pmp_mode_t A; + logic X; + logic W; + logic R; + } pmpcfg_base_t; + +typedef struct packed { + pmpcfg_base_t cfg3; + pmpcfg_base_t cfg2; + pmpcfg_base_t cfg1; + pmpcfg_base_t cfg0; +} pmpcfg_t; + +typedef logic [31:0] pmpaddr_t; + +endpackage + +`endif //PMP_TYPES_1_12_PKG_SV diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index ed2d63e1e..6293f58ee 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 06/16/2016 -* Description: Execute Stage for the Two Stage Pipeline +* Description: Execute Stage for the Two Stage Pipeline */ `include "tspp_fetch_execute_if.vh" @@ -54,11 +54,11 @@ module tspp_execute_stage( // Interface declarations control_unit_if cu_if(); - rv32i_reg_file_if rf_if(); + rv32i_reg_file_if rf_if(); alu_if alu_if(); jump_calc_if jump_if(); - branch_res_if branch_if(); - + branch_res_if branch_if(); + // Module instantiations control_unit cu ( .cu_if(cu_if), @@ -69,7 +69,7 @@ module tspp_execute_stage( .rmgmt_req_reg_r(rm_if.req_reg_r), .rmgmt_req_reg_w(rm_if.req_reg_w) ); - // generate + // generate // case (BASE_ISA) // "RV32I": rv32i_reg_file rf (CLK, nRST, rf_if); // "RV32E": rv32e_reg_file rf (CLK, nRST, rf_if); @@ -82,15 +82,15 @@ module tspp_execute_stage( end else begin: REG_FILE_SEL rv32i_reg_file rf (CLK, nRST, rf_if); - end + end endgenerate // rv32i_reg_file rf (.*); alu alu (.*); jump_calc jump_calc (.*); - + branch_res branch_res ( .br_if(branch_if) - ); + ); word_t store_swapped; endian_swapper store_swap ( @@ -131,16 +131,16 @@ module tspp_execute_stage( end endgenerate - //RV32C + //RV32C assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; assign rv32cif.halt = cu_if.halt; assign rv32cif.ex_busy = cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; - assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; - assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; /******************************************************* - *** Sign Extensions + *** Sign Extensions *******************************************************/ word_t imm_I_ext, imm_S_ext, imm_UJ_ext; assign imm_I_ext = {{20{cu_if.imm_I[11]}}, cu_if.imm_I}; @@ -148,7 +148,7 @@ module tspp_execute_stage( assign imm_S_ext = {{20{cu_if.imm_S[11]}}, cu_if.imm_S}; /******************************************************* - *** Jump Target Calculator and Associated Logic + *** Jump Target Calculator and Associated Logic *******************************************************/ word_t jump_addr /* verilator isolate_assignments */; always_comb begin @@ -161,22 +161,22 @@ module tspp_execute_stage( jump_if.offset = imm_I_ext; jump_addr = jump_if.jalr_addr; end - end + end /******************************************************* - *** ALU and Associated Logic + *** ALU and Associated Logic *******************************************************/ word_t imm_or_shamt; assign imm_or_shamt = (cu_if.imm_shamt_sel == 1'b1) ? cu_if.shamt : imm_I_ext; assign alu_if.aluop = cu_if.alu_op; logic mal_addr; - + always_comb begin case (cu_if.alu_a_sel) 2'd0: alu_if.port_a = rf_if.rs1_data; 2'd1: alu_if.port_a = imm_S_ext; 2'd2: alu_if.port_a = fetch_ex_if.fetch_ex_reg.pc; - 2'd3: alu_if.port_a = '0; //Not Used + 2'd3: alu_if.port_a = '0; //Not Used endcase end @@ -199,15 +199,15 @@ module tspp_execute_stage( 3'd2 : rf_if.w_data = cu_if.imm_U; 3'd3 : rf_if.w_data = alu_if.port_out; 3'd4 : rf_if.w_data = prv_pipe_if.rdata; - default : rf_if.w_data = '0; + default : rf_if.w_data = '0; endcase end end - assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) & (~hazard_if.if_ex_stall | hazard_if.npc_sel | rv32cif.done_earlier) & - ~(cu_if.dren & mal_addr); + assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) & (~hazard_if.if_ex_stall | hazard_if.npc_sel | rv32cif.done_earlier) & + ~(cu_if.dren & mal_addr); /******************************************************* - *** Branch Target Resolution and Associated Logic + *** Branch Target Resolution and Associated Logic *******************************************************/ word_t resolved_addr; @@ -227,16 +227,16 @@ module tspp_execute_stage( assign resolved_addr = branch_if.branch_taken ? branch_addr : fetch_ex_if.fetch_ex_reg.pc4; - + assign fetch_ex_if.brj_addr = ((cu_if.ex_pc_sel == 1'b1) && ~rm_if.req_br_j) ? jump_addr : resolved_addr; - + assign hazard_if.mispredict = fetch_ex_if.fetch_ex_reg.prediction ^ branch_taken; - + /******************************************************* - *** Data Ram Interface Logic + *** Data Ram Interface Logic *******************************************************/ logic [1:0] byte_offset; @@ -250,8 +250,8 @@ module tspp_execute_stage( assign dgen_bus_if.byte_en = byte_en; assign dgen_bus_if.addr = rm_if.req_mem ? rm_if.mem_addr : alu_if.port_out; assign hazard_if.d_mem_busy = dgen_bus_if.busy; - assign byte_offset = alu_if.port_out[1:0]; - + assign byte_offset = alu_if.port_out[1:0]; + always_comb begin dgen_bus_if.wdata = '0; if (rm_if.req_mem) @@ -260,15 +260,15 @@ module tspp_execute_stage( case(cu_if.load_type) // load_type can be used for store_type as well LB: dgen_bus_if.wdata = {4{rf_if.rs2_data[7:0]}}; LH: dgen_bus_if.wdata = {2{rf_if.rs2_data[15:0]}}; - LW: dgen_bus_if.wdata = rf_if.rs2_data; + LW: dgen_bus_if.wdata = rf_if.rs2_data; endcase end end - // Assign byte_en based on load type + // Assign byte_en based on load type // funct3 for loads and stores are the same bit positions - // byte_en is valid for both loads and stores + // byte_en is valid for both loads and stores always_comb begin unique case(cu_if.load_type) LB : begin @@ -321,7 +321,7 @@ module tspp_execute_stage( else ifence_reg <= cu_if.ifence; end - + assign ifence_pulse = cu_if.ifence && ~ifence_reg; assign cc_if.icache_flush = ifence_pulse; assign cc_if.icache_clear = 1'b0; @@ -352,7 +352,7 @@ module tspp_execute_stage( assign hazard_if.fence_stall = cu_if.ifence && (~dflushed || ~iflushed); /******************************************************* - *** Hazard Unit Interface Logic + *** Hazard Unit Interface Logic *******************************************************/ assign hazard_if.dren = cu_if.dren; assign hazard_if.dwen = cu_if.dwen; @@ -365,33 +365,33 @@ module tspp_execute_stage( halt <= 1'b0; else if (cu_if.halt) halt <= cu_if.halt; - end + end /******************************************************* - *** CSR / Priv Interface Logic - *******************************************************/ + *** CSR / Priv Interface Logic + *******************************************************/ assign prv_pipe_if.swap = cu_if.csr_swap; assign prv_pipe_if.clr = cu_if.csr_clr; assign prv_pipe_if.set = cu_if.csr_set; assign prv_pipe_if.wdata = cu_if.csr_imm ? {27'h0, cu_if.zimm} : rf_if.rs1_data; - assign prv_pipe_if.addr = cu_if.csr_addr; + assign prv_pipe_if.csr_addr = cu_if.csr_addr; assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr | prv_pipe_if.set) & ~hazard_if.if_ex_stall; assign prv_pipe_if.instr = (cu_if.instr != '0); always_comb begin - if(byte_en == 4'hf) + if(byte_en == 4'hf) mal_addr = (dgen_bus_if.addr[1:0] != 2'b00); else if (byte_en == 4'h3 || byte_en == 4'hc) begin mal_addr = (dgen_bus_if.addr[1:0] == 2'b01 || dgen_bus_if.addr[1:0] == 2'b11); end - else + else mal_addr = 1'b0; end //Send exceptions to Hazard Unit assign hazard_if.illegal_insn = (cu_if.illegal_insn & ~rm_if.ex_token) | prv_pipe_if.invalid_csr; - assign hazard_if.fault_l = 1'b0; + assign hazard_if.fault_l = 1'b0; assign hazard_if.mal_l = cu_if.dren & mal_addr; assign hazard_if.fault_s = 1'b0; assign hazard_if.mal_s = cu_if.dwen & mal_addr; @@ -420,7 +420,7 @@ module tspp_execute_stage( assign sparce_if.sasa_addr = alu_if.port_out; assign sparce_if.sasa_wen = cu_if.dwen; assign sparce_if.rd = rf_if.rd; - + /********************************************************* *** Signals for Bind Tracking - Read-Only, These don't affect execution *********************************************************/ @@ -435,4 +435,3 @@ module tspp_execute_stage( assign instr_30 = cu_if.instr[30]; endmodule - diff --git a/source_code/pipelines/tspp/tspp_fetch_stage.sv b/source_code/pipelines/tspp/tspp_fetch_stage.sv index 3d6c141e0..6264004d9 100644 --- a/source_code/pipelines/tspp/tspp_fetch_stage.sv +++ b/source_code/pipelines/tspp/tspp_fetch_stage.sv @@ -41,7 +41,7 @@ module tspp_fetch_stage ( ); import rv32i_types_pkg::*; - ///parameter RESET_PC = 32'h200; + //parameter RESET_PC = 32'h200; parameter RESET_PC = 32'h80000000; word_t pc, pc4or2, npc, instr; diff --git a/source_code/privs/priv_1_12/priv_1_12_block.sv b/source_code/privs/priv_1_12/priv_1_12_block.sv new file mode 100644 index 000000000..63ffeb2e3 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_block.sv @@ -0,0 +1,112 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_block.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/27/2022 +* Description: Top level block for the privileged unit, v1.12 +*/ + +`include "prv_pipeline_if.vh" +`include "priv_1_12_internal_if.vh" +`include "core_interrupt_if.vh" + +module priv_1_12_block ( + input logic CLK, nRST, + prv_pipeline_if.priv_block prv_pipe_if, + core_interrupt_if.core interrupt_if +); + + import machine_mode_types_1_12_pkg::*; + + priv_1_12_internal_if prv_intern_if(); + + priv_1_12_csr csr (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if)); + priv_1_12_int_ex_handler int_ex_handler (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if)); + priv_1_12_pipe_control pipe_ctrl (.prv_intern_if(prv_intern_if)); + + priv_level_t curr_priv; + + assign prv_intern_if.curr_priv = M_MODE; // TODO make this changeable + + // Assign CSR values + assign prv_intern_if.inst_ret = prv_pipe_if.wb_enable & prv_pipe_if.instr; + assign prv_intern_if.csr_addr = prv_pipe_if.csr_addr; + assign prv_intern_if.csr_write = prv_pipe_if.swap; + assign prv_intern_if.csr_clear = prv_pipe_if.clr; + assign prv_intern_if.csr_set = prv_pipe_if.set; + assign prv_intern_if.new_csr_val = prv_pipe_if.wdata; + assign prv_pipe_if.rdata = prv_intern_if.old_csr_val; + assign prv_pipe_if.invalid_csr = prv_intern_if.invalid_csr; + + // Disable interrupts that will not be used + assign prv_intern_if.timer_int_u = 1'b0; + assign prv_intern_if.timer_int_s = 1'b0; + assign prv_intern_if.timer_int_m = interrupt_if.timer_int; + assign prv_intern_if.soft_int_u = 1'b0; + assign prv_intern_if.soft_int_s = 1'b0; + assign prv_intern_if.soft_int_m = interrupt_if.soft_int; + assign prv_intern_if.ext_int_u = 1'b0; + assign prv_intern_if.ext_int_s = 1'b0; + assign prv_intern_if.ext_int_m = interrupt_if.ext_int; + + // Disable clear interrupts that will not be used + assign prv_intern_if.clear_timer_int_u = 1'b0; + assign prv_intern_if.clear_timer_int_s = 1'b0; + assign prv_intern_if.clear_timer_int_m = interrupt_if.timer_int_clear; + assign prv_intern_if.clear_soft_int_u = 1'b0; + assign prv_intern_if.clear_soft_int_s = 1'b0; + assign prv_intern_if.clear_soft_int_m = interrupt_if.soft_int_clear; + assign prv_intern_if.clear_ext_int_u = 1'b0; + assign prv_intern_if.clear_ext_int_s = 1'b0; + assign prv_intern_if.clear_ext_int_m = interrupt_if.ext_int_clear; + + // from pipeline to the priv unit + assign prv_intern_if.pipe_clear = prv_pipe_if.pipe_clear; + assign prv_intern_if.epc = prv_pipe_if.epc; + assign prv_intern_if.fault_insn_access = prv_pipe_if.fault_insn; + assign prv_intern_if.mal_insn = prv_pipe_if.mal_insn; + assign prv_intern_if.illegal_insn = prv_pipe_if.illegal_insn; + assign prv_intern_if.fault_l = prv_pipe_if.fault_l; + assign prv_intern_if.mal_l = prv_pipe_if.mal_l; + assign prv_intern_if.fault_s = prv_pipe_if.fault_s; + assign prv_intern_if.mal_s = prv_pipe_if.mal_s; + assign prv_intern_if.breakpoint = prv_pipe_if.breakpoint; + assign prv_intern_if.env_m = prv_pipe_if.env_m; + assign prv_intern_if.env_s = 1'b0; + assign prv_intern_if.env_u = 1'b0; + assign prv_intern_if.fault_insn_page = 1'b0; + assign prv_intern_if.fault_load_page = 1'b0; + assign prv_intern_if.fault_store_page = 1'b0; + assign prv_intern_if.curr_mtval = prv_pipe_if.badaddr; + assign prv_intern_if.valid_write = prv_pipe_if.valid_write; + assign prv_intern_if.mret = prv_pipe_if.ret; // TODO make this changeable + assign prv_intern_if.sret = 1'b0; + assign prv_intern_if.uret = 1'b0; + + // RISC-MGMT? + // not sure what these are for, part of priv 1.11 + assign prv_intern_if.ex_rmgmt = prv_pipe_if.ex_rmgmt; + assign prv_intern_if.ex_rmgmt_cause = prv_pipe_if.ex_rmgmt_cause; + + // from priv unit to pipeline + assign prv_pipe_if.priv_pc = prv_intern_if.priv_pc; + assign prv_pipe_if.insert_pc = prv_intern_if.insert_pc; + assign prv_pipe_if.intr = prv_intern_if.intr; + +endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_csr.sv b/source_code/privs/priv_1_12/priv_1_12_csr.sv new file mode 100644 index 000000000..853ae390e --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_csr.sv @@ -0,0 +1,392 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_csr.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/28/2022 +* Description: CSR File for Priv Unit 1.12 +*/ + +`include "priv_1_12_internal_if.vh" +`include "priv_ext_if.vh" +`include "component_selection_defines.vh" + +module priv_1_12_csr # ( + HARTID = 0 ) ( + input CLK, nRST, + priv_1_12_internal_if.csr prv_intern_if + `ifdef RV32F_SUPPORTED + , priv_ext_if.priv priv_ext_f_if + `endif // RV32F_SUPPORTED + `ifdef RV32V_SUPPORTED + , priv_ext_if.priv priv_ext_v_if + `endif // RV32V_SUPPORTED +); + + + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + /* Machine Information */ + csr_reg_t mvendorid; + csr_reg_t marchid; + csr_reg_t mimpid; + csr_reg_t mhartid; + csr_reg_t mconfigptr; + /* Machine Trap Setup */ + mstatus_t mstatus, mstatus_next; + misa_t misa; + mie_t mie, mie_next; + mtvec_t mtvec, mtvec_next; + mstatush_t mstatush; + /* Machine Trap Handling */ + csr_reg_t mscratch, mscratch_next; + csr_reg_t mepc, mepc_next; + mcause_t mcause, mcause_next; + csr_reg_t mtval, mtval_next; + mip_t mip, mip_next; + /* Machine Counters/Timers */ + mcounteren_t mcounteren, mcounteren_next; + mcountinhibit_t mcounterinhibit, mcounterinhibit_next; + csr_reg_t mcycle; + csr_reg_t minstret; + csr_reg_t mcycleh; + csr_reg_t minstreth; + long_csr_t cycles_full, cf_next; + long_csr_t instret_full, if_next; + + csr_reg_t nxt_csr_val; + + // invalid_csr flags + logic invalid_csr_0, invalid_csr_1; + assign prv_intern_if.invalid_csr = invalid_csr_0 | invalid_csr_1; + + // Extension Broadcast Signals + `ifdef RV32F_SUPPORTED + assign priv_ext_f_if.csr_addr = prv_intern_if.csr_addr; + assign priv_ext_f_if.value_in = nxt_csr_val; + assign priv_ext_f_if.csr_active = prv_intern_if.valid_write & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); + `endif // RV32F_SUPPORTED + `ifdef RV32V_SUPPORTED + assign priv_ext_v_if.csr_addr = prv_intern_if.csr_addr; + assign priv_ext_v_if.value_in = nxt_csr_val; + assign priv_ext_v_if.csr_active = prv_intern_if.valid_write & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); + `endif // RV32V_SUPPORTED + + /* Save some logic with this */ + assign mcycle = cycles_full[31:0]; + assign mcycleh = cycles_full[63:32]; + assign minstret = instret_full[31:0]; + assign minstreth = instret_full[63:32]; + + /* These info registers are always just tied to certain values */ + assign mvendorid = '0; + assign marchid = '0; + assign mimpid = '0; + assign mconfigptr = '0; + assign mhartid = HARTID; + + /* These registers are RO fields */ + assign misa.zero = '0; + assign misa.base = BASE_RV32; + // NOTE: Per the v1.12 spec, both I and E CANNOT be high - If supporting E, I must be disabled + assign misa.extensions = MISA_EXT_I + `ifdef RV32C_SUPPORTED + | MISA_EXT_C + `endif `ifdef RV32E_SUPPORTED + | MISA_EXT_E + `endif `ifdef RV32F_SUPPORTED + | MISA_EXT_F + `endif `ifdef RV32M_SUPPORTED + | MISA_EXT_M + `endif `ifdef RV32U_SUPPORTED + | MISA_EXT_U + `endif `ifdef RV32V_SUPPORTED + | MISA_EXT_V + `endif `ifdef CUSTOM_SUPPORTED + | MISA_EXT_X + `endif; + + assign mstatush.reserved_0 = '0; + assign mstatush.sbe = 1'b0; + assign mstatush.mbe = 1'b0; + assign mstatush.reserved_1 = '0; + + + + // Control and Status Registers + always_ff @ (posedge CLK, negedge nRST) begin + if (~nRST) begin + /* mstatus reset */ + mstatus.mie <= 1'b0; + mstatus.mpie <= 1'b0; + mstatus.mpp <= M_MODE; + mstatus.mprv <= 1'b0; + mstatus.tw <= 1'b1; + mstatus.reserved_0 <= '0; + mstatus.reserved_1 <= '0; + mstatus.reserved_2 <= '0; + mstatus.reserved_3 <= '0; + mstatus.sie <= 1'b0; + mstatus.spie <= 1'b0; + mstatus.ube <= 1'b0; + mstatus.spp <= 1'b0; + mstatus.sum <= 1'b0; + mstatus.mxr <= 1'b0; + mstatus.tvm <= 1'b0; + mstatus.tsr <= 1'b0; + mstatus.sd <= &(mstatus.vs) | &(mstatus.fs) | &(mstatus.xs); + `ifdef RV32V_SUPPORTED + mstatus.vs <= VS_INITIAL; + `else + mstatus.vs <= VS_OFF; + `endif + `ifdef RV32F_SUPPORTED + mstatus.fs <= FS_INITIAL; + `else + mstatus.fs <= FS_OFF; + `endif + `ifdef CUSTOM_SUPPORTED + mstatus.xs <= XS_NONE_D; + `else + mstatus.xs <= XS_ALL_OFF; + `endif + + /* mtvec reset */ + mtvec.mode <= DIRECT; + mtvec.base <= '0; + + /* mie reset */ + mie <= '0; + + /* mip reset */ + mip <= '0; + + /* msratch reset */ + mscratch <= '0; + + /* mepc reset */ + mepc <= '0; + + /* mtval reset */ + mtval <= '0; + + /* mcounter reset */ + mcounteren <= '1; + mcounterinhibit <= '0; + + /* perf mon reset */ + cycles_full <= '0; + instret_full <= '0; + + /* mcause reset */ + mcause <= '0; + + end else begin + mstatus <= mstatus_next; + mtvec <= mtvec_next; + mie <= mie_next; + mip <= mip_next; + mscratch <= mscratch_next; + mepc <= mepc_next; + mtval <= mtval_next; + mcounteren <= mcounteren_next; + mcounterinhibit <= mcounterinhibit_next; + mcause <= mcause_next; + cycles_full <= cf_next; + instret_full <= if_next; + end + end + + // Privilege Check and Legal Value Check + always_comb begin + mstatus_next = mstatus; + mtvec_next = mtvec; + mie_next = mie; + mip_next = mip; + mscratch_next = mscratch; + mepc_next = mepc; + mtval_next = mtval; + mcounteren_next = mcounteren; + mcounterinhibit_next = mcounterinhibit; + mcause_next = mcause_next; + + nxt_csr_val = (prv_intern_if.csr_write) ? prv_intern_if.new_csr_val : + (prv_intern_if.csr_set) ? prv_intern_if.new_csr_val | prv_intern_if.old_csr_val : + (prv_intern_if.csr_clear) ? ~prv_intern_if.new_csr_val & prv_intern_if.old_csr_val : + prv_intern_if.new_csr_val; + invalid_csr_0 = 1'b0; + + if (prv_intern_if.csr_addr[9:8] & prv_intern_if.curr_priv != 2'b11) begin + if (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear) begin + invalid_csr_0 = 1'b1; // Not enough privilege + end + end else begin + if (prv_intern_if.valid_write) begin + casez(prv_intern_if.csr_addr) + MSTATUS_ADDR: begin + if (prv_intern_if.new_csr_val[12:11] == 2'b10) begin + mstatus_next.mpp = U_MODE; // If invalid privilege level, dump at 0 + end else begin + mstatus_next.mpp = priv_level_t'(nxt_csr_val[12:11]); + end + mstatus_next.mie = nxt_csr_val[3]; + mstatus_next.mpie = nxt_csr_val[7]; + mstatus_next.mprv = nxt_csr_val[17]; + mstatus_next.tw = nxt_csr_val[21]; + end + + MTVEC_ADDR: begin + if (prv_intern_if.new_csr_val[1:0] > 2'b01) begin + mtvec_next.mode = DIRECT; + end else begin + mtvec_next.mode = vector_modes_t'(nxt_csr_val[1:0]); + end + mtvec_next.base = nxt_csr_val[31:2]; + end + + MIE_ADDR: begin + mie_next.msie = nxt_csr_val[3]; + mie_next.mtie = nxt_csr_val[7]; + mie_next.meie = nxt_csr_val[11]; + end + + MIP_ADDR: begin + mip_next.msip = nxt_csr_val[3]; + mip_next.mtip = nxt_csr_val[7]; + mip_next.meip = nxt_csr_val[11]; + end + MSCRATCH_ADDR: begin + mscratch_next = nxt_csr_val; + end + MEPC_ADDR: begin + mepc_next = nxt_csr_val; + end + MTVAL_ADDR: begin + mtval_next = nxt_csr_val; + end + MCOUNTEREN_ADDR: begin + mcounteren_next = nxt_csr_val; + end + MCOUNTINHIBIT_ADDR: begin + mcounterinhibit_next = nxt_csr_val; + end + MCAUSE_ADDR: begin + mcause_next = nxt_csr_val; + end + endcase + end + end + + // inject values + if (prv_intern_if.inject_mstatus) begin + mstatus_next = prv_intern_if.next_mstatus; + end + if (prv_intern_if.inject_mtval) begin + mtval_next = prv_intern_if.next_mtval; + end + if (prv_intern_if.inject_mepc) begin + mepc_next = prv_intern_if.next_mepc; + end + if (prv_intern_if.inject_mcause) begin + mcause_next = prv_intern_if.next_mcause; + end + if (prv_intern_if.inject_mie) begin + mie_next = prv_intern_if.next_mie; + end + if (prv_intern_if.inject_mip) begin + mip_next = prv_intern_if.next_mip; + end + end + + // hw perf mon + always_comb begin + cf_next = cycles_full; + if_next = instret_full; + + if (mcounteren.cy & ~mcounterinhibit.cy) begin + cf_next = cycles_full + 1; + end + if (mcounteren.ir & ~mcounterinhibit.ir) begin + if_next = instret_full + prv_intern_if.inst_ret; + end + end + + // Return proper values to CPU, PMP, PMA + always_comb begin + /* CPU return */ + prv_intern_if.old_csr_val = '0; + invalid_csr_1 = 1'b0; + casez(prv_intern_if.csr_addr) + MVENDORID_ADDR: prv_intern_if.old_csr_val = mvendorid; + MARCHID_ADDR: prv_intern_if.old_csr_val = marchid; + MIMPID_ADDR: prv_intern_if.old_csr_val = mimpid; + MHARTID_ADDR: prv_intern_if.old_csr_val = mhartid; + MCONFIGPTR_ADDR: prv_intern_if.old_csr_val = mconfigptr; + MSTATUS_ADDR: prv_intern_if.old_csr_val = mstatus; + MISA_ADDR: prv_intern_if.old_csr_val = misa; + MIE_ADDR: prv_intern_if.old_csr_val = mie; + MTVEC_ADDR: prv_intern_if.old_csr_val = mtvec; + MSTATUSH_ADDR: prv_intern_if.old_csr_val = mstatush; + MSCRATCH_ADDR: prv_intern_if.old_csr_val = mscratch; + MEPC_ADDR: prv_intern_if.old_csr_val = mepc; + MCAUSE_ADDR: prv_intern_if.old_csr_val = mcause; + MTVAL_ADDR: prv_intern_if.old_csr_val = mtval; + MIP_ADDR: prv_intern_if.old_csr_val = mip; + MCOUNTEREN_ADDR: prv_intern_if.old_csr_val = mcounteren; + MCOUNTINHIBIT_ADDR: prv_intern_if.old_csr_val = mcounterinhibit; + MCYCLE_ADDR: prv_intern_if.old_csr_val = mcycle; + MINSTRET_ADDR: prv_intern_if.old_csr_val = minstret; + MCYCLEH_ADDR: prv_intern_if.old_csr_val = mcycleh; + MINSTRETH_ADDR: prv_intern_if.old_csr_val = minstreth; + default: begin + if (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear) begin + `ifdef RV32F_SUPPORTED + if (priv_ext_f_if.ack) begin + prv_intern_if.old_csr_val = priv_ext_f_if.value_out; + end + `endif // RV32F_SUPPORTED + `ifdef RV32V_SUPPORTED + if (priv_ext_v_if.ack) begin + prv_intern_if.old_csr_val = priv_ext_v_if.value_out; + end + `endif // RV32V_SUPPORTED + + // CSR address doesn't exist + invalid_csr_1 = 1'b1 + `ifdef RV32F_SUPPORTED + & (~priv_ext_f_if.ack) & (~priv_ext_f_if.invalid_csr) + `endif // RV32F_SUPPORTED + `ifdef RV32V_SUPPORTED + & (~priv_ext_v_if.ack) & (~priv_ext_v_if.invalid_csr) + `endif // RV32V_SUPPORTED + ; + end + end + endcase + end + + /* Priv control return */ + assign prv_intern_if.curr_mip = mip; + assign prv_intern_if.curr_mie = mie; + assign prv_intern_if.curr_mcause = mcause; + assign prv_intern_if.curr_mepc = mepc; + assign prv_intern_if.curr_mstatus = mstatus; + assign prv_intern_if.curr_mtvec = mtvec; + +endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv new file mode 100644 index 000000000..f21420a75 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv @@ -0,0 +1,211 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_int_ex_handler.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 09/27/2022 +* Description: Main interrupt and exception handler block +*/ + +`include "priv_1_12_internal_if.vh" + +module priv_1_12_int_ex_handler ( + input CLK, nRST, + priv_1_12_internal_if.int_ex_handler prv_intern_if +); + + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + ex_code_t ex_src; + logic exception; + + int_code_t int_src; + logic interrupt, clear_interrupt; + // NOTE see the bottom for why these signals exist + logic interrupt_reg, interrupt_fired, update_mie; + + // Determine the source of the interrupt + always_comb begin + interrupt = 1'b1; + int_src = SOFT_INT_S; + + if (prv_intern_if.ext_int_m) begin + int_src = EXT_INT_M; + end + else if (prv_intern_if.soft_int_m) begin + int_src = SOFT_INT_M; + end + else if (prv_intern_if.timer_int_m) begin + int_src = TIMER_INT_M; + end + else if (prv_intern_if.ext_int_s) begin + int_src = EXT_INT_S; + end + else if (prv_intern_if.soft_int_s) begin + int_src = SOFT_INT_S; + end + else if (prv_intern_if.timer_int_s) begin + int_src = TIMER_INT_S; + end + else begin + interrupt = 1'b0; + end + end + + assign clear_interrupt = (prv_intern_if.clear_timer_int_m | prv_intern_if.clear_soft_int_m | prv_intern_if.clear_ext_int_m | prv_intern_if.clear_timer_int_s | prv_intern_if.clear_soft_int_s | prv_intern_if.clear_ext_int_s); + + // Determine whether an exception occured + always_comb begin + exception = 1'b1; + ex_src = INSN_MAL; + + if (prv_intern_if.breakpoint) + ex_src = BREAKPOINT; + else if (prv_intern_if.fault_insn_page) + ex_src = INSN_PAGE; + else if (prv_intern_if.fault_insn_access) + ex_src = INSN_ACCESS; + else if (prv_intern_if.illegal_insn) + ex_src = ILLEGAL_INSN; + else if (prv_intern_if.mal_insn) + ex_src = INSN_MAL; + else if (prv_intern_if.env_u) + ex_src = ENV_CALL_U; + else if (prv_intern_if.env_s) + ex_src = ENV_CALL_S; + else if (prv_intern_if.env_m) + ex_src = ENV_CALL_M; + else if (prv_intern_if.mal_s) + ex_src = S_ADDR_MAL; + else if (prv_intern_if.mal_l) + ex_src = L_ADDR_MAL; + else if (prv_intern_if.fault_store_page) + ex_src = STORE_PAGE; + else if (prv_intern_if.fault_load_page) + ex_src = LOAD_PAGE; + else if (prv_intern_if.fault_s) + ex_src = S_FAULT; + else if (prv_intern_if.fault_l) + ex_src = L_FAULT; + else if (prv_intern_if.ex_rmgmt) + ex_src = ex_code_t'(prv_intern_if.ex_rmgmt_cause); + else + exception = 1'b0; + end + + // Output info to pipe_ctrl + assign prv_intern_if.intr = exception | interrupt_reg; + assign interrupt_fired = (prv_intern_if.curr_mstatus.mie & ((prv_intern_if.curr_mie.mtie & prv_intern_if.curr_mip.mtip) | + (prv_intern_if.curr_mie.msie & prv_intern_if.curr_mip.msip) | (prv_intern_if.curr_mie.meie & prv_intern_if.curr_mip.meip))); + + // Register updates on Interrupts/Exceptions + /* NOTE a lot of the below code are patterns that exist to solve issues + on the 2 stage pipeline. This should be reimagined if we change pipelines. + Most of this is copied verbatim from the priv 1.11 unit */ + assign prv_intern_if.inject_mcause = exception | interrupt; + assign prv_intern_if.next_mcause.interrupt = ~exception; + assign prv_intern_if.next_mcause.cause = exception ? ex_src : int_src; + + assign prv_intern_if.inject_mip = interrupt | clear_interrupt; + always_comb begin + prv_intern_if.next_mip = prv_intern_if.curr_mip; + + if (prv_intern_if.ext_int_m) prv_intern_if.next_mip.meip = 1'b1; + else if (prv_intern_if.clear_ext_int_m) prv_intern_if.next_mip.meip = 1'b0; + + if (prv_intern_if.soft_int_m) prv_intern_if.next_mip.msip = 1'b1; + else if (prv_intern_if.clear_soft_int_m) prv_intern_if.next_mip.msip = 1'b0; + + if (prv_intern_if.timer_int_m) prv_intern_if.next_mip.mtip = 1'b1; + else if (prv_intern_if.clear_timer_int_m) prv_intern_if.next_mip.mtip = 1'b0; + + if (prv_intern_if.ext_int_s) prv_intern_if.next_mip.seip = 1'b1; + else if (prv_intern_if.clear_ext_int_s) prv_intern_if.next_mip.seip = 1'b0; + + if (prv_intern_if.soft_int_s) prv_intern_if.next_mip.ssip = 1'b1; + else if (prv_intern_if.clear_soft_int_s) prv_intern_if.next_mip.ssip = 1'b0; + + if (prv_intern_if.timer_int_s) prv_intern_if.next_mip.stip = 1'b1; + else if (prv_intern_if.clear_timer_int_s) prv_intern_if.next_mip.stip = 1'b0; + end + + assign prv_intern_if.inject_mstatus = exception | prv_intern_if.intr | prv_intern_if.mret | prv_intern_if.sret | prv_intern_if.uret; + + always_comb begin + prv_intern_if.next_mstatus.mie = prv_intern_if.curr_mstatus.mie; + prv_intern_if.next_mstatus.mpie = prv_intern_if.curr_mstatus.mpie; + if (update_mie) begin // interrupt has truly been registered and it is time to go to the vector table + prv_intern_if.next_mstatus.mpie = prv_intern_if.curr_mstatus.mie; // when a trap is taken mpie is set to the current mie + prv_intern_if.next_mstatus.mie = 1'b0; + end else if (prv_intern_if.mret) begin + prv_intern_if.next_mstatus.mpie = 1'b0; // leaving the vector table + prv_intern_if.next_mstatus.mie = prv_intern_if.curr_mstatus.mpie; + end + end + + // Update EPC as soon as interrupt or exception is found + // Note: mepc cannot update immediately, as if the processor is in an interrupt already, + // the MEPC captured will be within the interrupt (and nested interrupts are not supported). + // Interrupt fired notes when an interrupt is seen by the processor, i.e. when mstatus.mie is high again. + // The signal is 2 cycles long, so the update_mie signal is used to clip it down to 1 to prevent MEPC + // double update which results in skipping an instruction. + assign prv_intern_if.inject_mepc = exception | (interrupt_fired & ~update_mie); // TODO: Change to interrupt + assign prv_intern_if.next_mepc = prv_intern_if.epc; + + assign prv_intern_if.inject_mtval = (prv_intern_if.mal_l | prv_intern_if.fault_l | prv_intern_if.mal_s | prv_intern_if.fault_s | + prv_intern_if.illegal_insn | prv_intern_if.fault_insn_access | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) + & prv_intern_if.pipe_clear; // TODO: May need to insert other exception signals + assign prv_intern_if.next_mtval = prv_intern_if.curr_mtval; + + /* Interrupt needs to be latched until pipeline cleared */ + /* because mstatus.ie causes the irq to disappear after */ + /* one cycle. Cannot wait to clear mstatus.ie because */ + /* then another interrupt can fire during pipeline clear */ + always_ff @ (posedge CLK, negedge nRST) begin + if (!nRST) + interrupt_reg <= '0; + else if (interrupt_fired) + interrupt_reg <= 1'b1; + else if (prv_intern_if.pipe_clear) + interrupt_reg <= '0; + end + + /* + * Fix for MIE/MPIE issue. This used to be the same as 'interrupt_reg' above, + * but the above stays high for 2+ cycles (i.e. waiting for pipe_clear). + * This caused MPIE to update twice; the first update would set MPIE to 1, + * and the second would cause MPIE to return to 0. Then, after an MRET, + * MIE would not be restored since MPIE was lost. Additionally, shortening + * interrupt_reg was not an option since pipe_clear must be asserted for the + * PC to be inserted into the pipeline from the priv unit, so creating this + * extra register was the cleanest solution to ensuring MPIE updates exactly + * once. + */ + always_ff @(posedge CLK, negedge nRST) begin + if(!nRST) + update_mie <= '0; + else if(interrupt_fired && ~update_mie) + update_mie <= 1'b1; + else if (prv_intern_if.pipe_clear) + update_mie <= '0; + else + update_mie <= '0; + end + +endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv b/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv new file mode 100644 index 000000000..218be39f3 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv @@ -0,0 +1,50 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_pipe_control.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 09/27/2022 +* Description: Controls when the pipeline should jump to a different + address after an exception or interrupt +*/ + +`include "priv_1_12_internal_if.vh" + +module priv_1_12_pipe_control ( + priv_1_12_internal_if.pipe_ctrl prv_intern_if +); + + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + assign prv_intern_if.insert_pc = prv_intern_if.mret | prv_intern_if.sret | prv_intern_if.uret | + (prv_intern_if.pipe_clear & prv_intern_if.intr); + + always_comb begin + prv_intern_if.priv_pc = '0; + + if (prv_intern_if.intr) begin + if (prv_intern_if.curr_mtvec.mode == VECTORED & prv_intern_if.curr_mcause.interrupt) begin // vectored mode based on the interrupt source + prv_intern_if.priv_pc = (prv_intern_if.curr_mtvec.base << 2) + (prv_intern_if.curr_mcause.cause << 2); + end else + prv_intern_if.priv_pc = prv_intern_if.curr_mtvec.base << 2; + end else if (prv_intern_if.mret) + prv_intern_if.priv_pc = prv_intern_if.curr_mepc; // when leaving the ISR, restore to the original PC + end + +endmodule diff --git a/source_code/privs/priv_wrapper.sv b/source_code/privs/priv_wrapper.sv index 7eed20e5e..bd46c2022 100644 --- a/source_code/privs/priv_wrapper.sv +++ b/source_code/privs/priv_wrapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 05/18/2017 -* Description: Top level wrapper for the priv ISA implementation. +* Description: Top level wrapper for the priv ISA implementation. */ `include "prv_pipeline_if.vh" @@ -31,7 +31,6 @@ module priv_wrapper ( core_interrupt_if.core interrupt_if ); - //TODO: Select different priv blocks for backwards compatibility - priv_1_11_block priv_block_i(.*); + priv_1_12_block priv_block_i(.*); endmodule diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index b482c5aed..c053e3bc9 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,7 +20,7 @@ * Email: steven69@purdue.edu * Date Created: 06/09/2016 * Description: The control unit combinationally sets all of the control -* signals used in the processor based on the incoming instruction. +* signals used in the processor based on the incoming instruction. */ `include "control_unit_if.vh" @@ -28,16 +28,16 @@ `include "risc_mgmt_if.vh" `include "decompressor_if.vh" -module control_unit +module control_unit ( control_unit_if.control_unit cu_if, rv32i_reg_file_if.cu rf_if, input logic [4:0] rmgmt_rsel_s_0, rmgmt_rsel_s_1, rmgmt_rsel_d, - input logic rmgmt_req_reg_r, rmgmt_req_reg_w + input logic rmgmt_req_reg_r, rmgmt_req_reg_w ); import alu_types_pkg::*; import rv32i_types_pkg::*; - import machine_mode_types_1_11_pkg::*; + import machine_mode_types_1_12_pkg::*; stype_t instr_s; itype_t instr_i; @@ -56,9 +56,9 @@ module control_unit assign cu_if.opcode = opcode_t'(cu_if.instr[6:0]); assign rf_if.rs1 = rmgmt_req_reg_r ? rmgmt_rsel_s_0 : cu_if.instr[19:15]; assign rf_if.rs2 = rmgmt_req_reg_r ? rmgmt_rsel_s_1 : cu_if.instr[24:20]; - assign rf_if.rd = rmgmt_req_reg_w ? rmgmt_rsel_d : cu_if.instr[11:7]; + assign rf_if.rd = rmgmt_req_reg_w ? rmgmt_rsel_d : cu_if.instr[11:7]; assign cu_if.shamt = cu_if.instr[24:20]; - + // Assign the immediate values assign cu_if.imm_I = instr_i.imm11_00; assign cu_if.imm_S = {instr_s.imm11_05, instr_s.imm04_00}; @@ -137,7 +137,7 @@ module control_unit assign sr = ((cu_if.opcode == IMMED && instr_i.funct3 == SRI) || (cu_if.opcode == REGREG && instr_r.funct3 == SR)); assign add_sub = (cu_if.opcode == REGREG && instr_r.funct3 == ADDSUB); - + assign aluop_sll = ((cu_if.opcode == IMMED && instr_i.funct3 == SLLI) || (cu_if.opcode == REGREG && instr_r.funct3 == SLL)); assign aluop_sra = sr && cu_if.instr[30]; @@ -196,7 +196,7 @@ module control_unit endgenerate // Privilege Control Signals assign cu_if.fault_insn = '0; - + always_comb begin case(cu_if.opcode) REGREG: cu_if.illegal_insn = instr_r.funct7[0]; @@ -207,7 +207,7 @@ module control_unit default : cu_if.illegal_insn = 1'b1; endcase end - + //Decoding of System Priv Instructions always_comb begin cu_if.ret_insn = 1'b0; @@ -242,7 +242,7 @@ module control_unit end else if (rv32i_system_t'(instr_r.funct3) == CSRRS) begin cu_if.csr_set = 1'b1; - end else if (rv32i_system_t'(instr_r.funct3) == CSRRC) begin + end else if (rv32i_system_t'(instr_r.funct3) == CSRRC) begin cu_if.csr_clr = 1'b1; end else if (rv32i_system_t'(instr_r.funct3) == CSRRWI) begin cu_if.csr_swap = 1'b1; @@ -262,4 +262,3 @@ module control_unit assign cu_if.zimm = cu_if.instr[19:15]; endmodule - diff --git a/source_code/tb/tb_priv_1_12_block.sv b/source_code/tb/tb_priv_1_12_block.sv new file mode 100644 index 000000000..ba8021b8f --- /dev/null +++ b/source_code/tb/tb_priv_1_12_block.sv @@ -0,0 +1,303 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_priv_1_12_block.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 04/02/2022 +* Description: Testbench for running the privileged unit v1.12. +*/ + +`timescale 1ns/1ns + +`include "rv32i_types_pkg.sv" +`include "machine_mode_types_1_12_pkg.sv" +`include "prv_pipeline_if.vh" +`include "priv_1_12_internal_if.vh" +`include "core_interrupt_if.vh" + +`define OUTPUT_FILE_NAME "cpu.hex" +`define STATS_FILE_NAME "stats.txt" +`define RVB_CLK_TIMEOUT 10000 + +module tb_priv_1_12_block (); + + parameter PERIOD = 10; + + localparam PROP_DELAY = 1ns; + + logic CLK, nRST; + logic ram_control; // 1 -> CORE, 0 -> TB + logic halt; + logic [31:0] addr, data_temp, data; + logic [63:0] hexdump_temp; + logic [7:0] checksum; + integer fptr, stats_ptr; + + integer test_num; + + //Interface Instantiations + prv_pipeline_if prv_pipeline_if(); + core_interrupt_if core_int_if(); + + // Package Instantiations + import machine_mode_types_1_12_pkg::*; + + //Module Instantiations + priv_1_12_block DUT ( + .CLK(CLK), + .nRST(nRST), + .prv_pipe_if(prv_pipeline_if), + .interrupt_if(core_int_if) + ); + + //Clock generation + initial begin : INIT + CLK = 0; + end : INIT + + always begin : CLOCK_GEN + #(PERIOD/2) CLK = ~CLK; + end : CLOCK_GEN + + //Setup core and let it run + initial begin : CORE_RUN + $display("\n"); + $display("==== Starting tests"); + + nRST = 0; + test_num = 0; + + prv_pipeline_if.pipe_clear = '0; + prv_pipeline_if.ret = '0; + prv_pipeline_if.epc = '0; + prv_pipeline_if.fault_insn = '0; + prv_pipeline_if.mal_insn = '0; + prv_pipeline_if.illegal_insn = '0; + prv_pipeline_if.fault_l = '0; + prv_pipeline_if.mal_l = '0; + prv_pipeline_if.fault_s = '0; + prv_pipeline_if.mal_s = '0; + prv_pipeline_if.breakpoint = '0; + prv_pipeline_if.env_m = '0; + prv_pipeline_if.badaddr = '0; + prv_pipeline_if.swap = '0; + prv_pipeline_if.clr = '0; + prv_pipeline_if.set = '0; + prv_pipeline_if.wdata = '0; + prv_pipeline_if.csr_addr = MISA_ADDR; + prv_pipeline_if.valid_write = '0; + prv_pipeline_if.wb_enable = '0; + prv_pipeline_if.instr = '0; + prv_pipeline_if.ex_rmgmt = '0; + prv_pipeline_if.ex_rmgmt_cause = '0; + + @(posedge CLK); + @(posedge CLK); + nRST = 1; + + $display("=== DUT reset"); + + @(posedge CLK); + #PROP_DELAY; + + $display("== CSR cases"); + // **************** + // Test Case 0: Read mvendorid + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MVENDORID_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 1: Read marchid + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MARCHID_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %b) (expected %b)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 2: Read mimpid + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MIMPID_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %b) (expected %b)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 3: Read mhartid + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MHARTID_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %b) (expected %b)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 4: Read mconfigptr + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MCONFIGPTR_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %b) (expected %b)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 5: Emulate CSRRW to mstatus + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.wdata = 32'h1088; + prv_pipeline_if.csr_addr = MSTATUS_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'h201800) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'h201800); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 6: Emulate CSRRW to mstatus pt2 + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.wdata = 32'h00001088; + prv_pipeline_if.csr_addr = MSTATUS_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'h88) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'h88); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 7: Emulate CSRRW to mscratch + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.wdata = 32'hdeadbeef; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'h0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'h0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 8: Emulate CSRRS to mscratch + // *************** + prv_pipeline_if.swap = 1'b0; + prv_pipeline_if.set = 1'b1; + prv_pipeline_if.wdata = 32'h21524110; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'hdeadbeef) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'hdeadbeef); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 9: Emulate CSRRS to mscratch pt2 + // *************** + prv_pipeline_if.set = 1'b1; + prv_pipeline_if.wdata = 32'h21524110; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'hffffffff) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'hffffffff); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 10: Emulate CSRRC to mscratch + // *************** + prv_pipeline_if.set = 1'b0; + prv_pipeline_if.clr = 1'b1; + prv_pipeline_if.wdata = 32'hffffffff; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'hffffffff) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'hffffffff); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 11: Emulate CSRRC to mscratch pt2 + // *************** + prv_pipeline_if.clr = 1'b1; + prv_pipeline_if.wdata = 32'h0; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'h0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'h0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + + $display("==== Finishing tests"); + $display("\n"); + $finish; + + end : CORE_RUN + +endmodule diff --git a/tb_core.cc b/tb_core.cc index 6d4291470..b89154b79 100644 --- a/tb_core.cc +++ b/tb_core.cc @@ -10,8 +10,31 @@ #include "Vtop_core.h" #include "Vtop_core_top_core.h" +#define MTIME_ADDR 0x10000 +#define MTIMEH_ADDR 0x10004 +#define MTIMECMP_ADDR 0x10008 +#define MTIMECMPH_ADDR 0x1000C +#define MSIP_ADDR 0x10010 +#define EXT_ADDR_SET 0x10014 +#define EXT_ADDR_CLEAR 0x10018 +#define MAGIC_ADDR 0x20000 + +// Inclusive range of memory-mapped peripherals +#define MMIO_RANGE_BEGIN (MTIME_ADDR) +#define MMIO_RANGE_END (MAGIC_ADDR) + +// doubles as mtime counter vluint64_t sim_time = 0; +/* + * Emulate memory-mapped CSRs + */ +uint64_t mtimecmp = 0; +uint32_t msip = 0; + +// Interrupt signals +bool ext_int = false; + class MemoryMap { @@ -58,6 +81,60 @@ class MemoryMap { } } + uint32_t mmio_read(uint32_t addr) { + uint32_t value = 0xBAD1BAD1; + switch(addr) { + case MTIME_ADDR: value = (uint32_t)(sim_time & 0xFFFFFFFF); + break; + case MTIMEH_ADDR: value = (uint32_t)(sim_time >> 32); + break; + case MTIMECMP_ADDR: value = (uint32_t)(mtimecmp & 0xFFFFFFFF); + break; + case MTIMECMPH_ADDR: value = (uint32_t)(mtimecmp >> 32); + break; + case MSIP_ADDR: value = msip; + break; + case EXT_ADDR_SET: value = ext_int; // Marker value for peripheral memory + break; + case EXT_ADDR_CLEAR: value = 0; // Clear 'register' doesn't return a value + break; + case MAGIC_ADDR: value = 0; // TODO: Should this be able to take stdin inputs? Any use? + break; + default: std::cout << "Warning: read of empty MMIO region @ " << std::hex << addr << std::dec << std::endl; + }; + + + return value; + } + + inline static bool is_mmio_region(uint32_t addr) { + return (addr >= MMIO_RANGE_BEGIN && addr <= MMIO_RANGE_END); + } + + void mmio_write(uint32_t addr, uint32_t value, uint8_t mask) { + auto mask_exp = expand_mask(mask); + + if(addr == MTIMECMP_ADDR) { + uint32_t mtimecmp_l = (uint32_t)(mtimecmp & 0xFFFFFFFF); + mtimecmp_l = (value & mask_exp) | (mtimecmp_l & ~mask_exp); + mtimecmp = (mtimecmp & 0xFFFFFFFF00000000) | ((uint64_t)mtimecmp_l); + } else if(addr == MTIMECMPH_ADDR) { + uint32_t mtimecmp_h = (uint32_t)(mtimecmp >> 32); + mtimecmp_h = (value & mask_exp) | (mtimecmp_h & ~mask_exp); + mtimecmp = (mtimecmp & 0xFFFFFFFF) | (((uint64_t)mtimecmp_h) << 32); + } else if(addr == MSIP_ADDR) { + msip = value; + } else if(addr == EXT_ADDR_SET) { + ext_int = true; + } else if(addr == EXT_ADDR_CLEAR) { + ext_int = false; + } else if(addr == MAGIC_ADDR) { + std::cout << (char)(value & 0xFF); + } else { + std::cout << "Warning: write to read-only MMIO region @ " << std::hex << addr << std::dec << std::endl; + } + } + // TODO: Add simulation for SWI/mtime? uint32_t read(uint32_t addr) { auto it = mmap.find(addr); @@ -103,6 +180,35 @@ class MemoryMap { } }; +void update_interrupt_signals(Vtop_core& dut) { + if(msip & 0x1) { + dut.soft_int = 1; + } else if(dut.soft_int) { + dut.soft_int = 0; + dut.soft_int_clear = 1; + } else if(dut.soft_int_clear) { + dut.soft_int_clear = 0; + } + + if(mtimecmp <= sim_time) { + dut.timer_int = 1; + } else if(dut.timer_int) { + dut.timer_int = 0; + dut.timer_int_clear = 1; + } else if(dut.timer_int_clear) { + dut.timer_int_clear = 0; + } + + if(ext_int) { + dut.ext_int = 1; + } else if(dut.ext_int) { + dut.ext_int = 0; + dut.ext_int_clear = 1; + } else if(dut.ext_int_clear) { + dut.ext_int_clear = 0; + } +} + void tick(Vtop_core& dut, VerilatedFstC& trace) { dut.CLK = 0; dut.eval(); @@ -157,27 +263,36 @@ int main(int argc, char **argv) { reset(dut, m_trace); - while(!dut.halt && sim_time < 1000000) { + while(!dut.halt && sim_time < 100000) { // TODO: Variable latency if((dut.ren || dut.wen) && dut.busy) { dut.busy = 0; if(dut.ren) { uint32_t addr = dut.addr & 0xFFFFFFFC; - dut.rdata = memory.read(addr); + if(!MemoryMap::is_mmio_region(addr)) { + dut.rdata = memory.read(addr); + } else { + dut.rdata = memory.mmio_read(addr); + } } else if(dut.wen) { uint32_t addr = dut.addr & 0xFFFFFFFC; uint32_t value = dut.wdata; uint8_t mask = dut.byte_en; - memory.write(addr, value, mask); + if(!MemoryMap::is_mmio_region(addr)) { + memory.write(addr, value, mask); + } else { + memory.mmio_write(addr, value, mask); + } } } else if(!dut.busy) { dut.busy = 1; } tick(dut, m_trace); + update_interrupt_signals(dut); } - if(sim_time == 1000000) { + if(sim_time >= 100000) { std::cout << "Test TIMED OUT" << std::endl; } else if(dut.top_core->get_x28() == 1) { std::cout << "Test PASSED" << std::endl; @@ -186,6 +301,7 @@ int main(int argc, char **argv) { } m_trace.close(); memory.dump(); + dut.final(); return 0; } diff --git a/verification/interrupts-exceptions/build_all.py b/verification/interrupts-exceptions/build_all.py new file mode 100755 index 000000000..f306e6c5e --- /dev/null +++ b/verification/interrupts-exceptions/build_all.py @@ -0,0 +1,51 @@ +#! /usr/bin/python3 + +import subprocess +import glob +import os +import pathlib + + +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i', '-mabi=ilp32', '-mcmodel=medany', + '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] + +cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] + + +if not os.path.isfile('./start.S') or not os.path.isfile('link.ld'): + print('Error: Could not find AFTx06.S or link.ld in this directory') + exit(1) + + +for fname in (glob.glob('./*.c') + glob.glob('./*.S')): + if 'start' in fname or 'utility' in fname: + print("Skipping {} as top-level file, appears to be a utility".format(fname)); + continue + print('Compiling {}'.format(fname)) + basename = pathlib.Path(fname).stem + + rv = subprocess.run(compile_cmd + [fname, '-o', basename + '.elf'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + print('Converting {} to binary'.format(fname)) + rv = subprocess.run(cvt_cmd + [basename + '.elf', basename + '.bin']) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + +print( +''' + Finished compilation. Now, pass the '.bin' file corresponding to + the example to run as an argument to 'VbASIC_wrapper' to run an example! +''') diff --git a/verification/interrupts-exceptions/ecall.c b/verification/interrupts-exceptions/ecall.c new file mode 100644 index 000000000..51eb9778a --- /dev/null +++ b/verification/interrupts-exceptions/ecall.c @@ -0,0 +1,41 @@ + +#include +#include "utility.h" + +extern volatile int flag; + + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + /* + asm volatile( + "mv t0, zero; csrrs t0, mepc, t0; addi t0, t0, 4; csrw mepc, t0;" + : + : + : "t0" + );*/ + print("Made it to handler!\n"); + flag = 1; +} + + +int main() { + + uint32_t mtvec_value = (uint32_t)handler; + uint32_t mstatus_value = 0x8; + + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + print("Jumping to handler...\n"); + asm volatile("ecall"); + + print("Flag is 0x"); + put_uint32_hex(flag); + print("\n"); + + return 0; +} diff --git a/verification/interrupts-exceptions/link.ld b/verification/interrupts-exceptions/link.ld new file mode 100755 index 000000000..4fd20ea69 --- /dev/null +++ b/verification/interrupts-exceptions/link.ld @@ -0,0 +1,14 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x0000000080000000; + .text.init : { *(.text.init) } + .text : { *(.text) } + .data ALIGN(0x1000) : { *(.data) } + .bss : { *(.bss) } + . = 0x0000000080007000; + .statuses : { *(.statuses) } + _end = .; +} diff --git a/verification/interrupts-exceptions/start.S b/verification/interrupts-exceptions/start.S new file mode 100644 index 000000000..9e588dd07 --- /dev/null +++ b/verification/interrupts-exceptions/start.S @@ -0,0 +1,17 @@ + +.global _start +_start: + li sp, 0x8000F000 + jal main + +.global done +done: + la x28, flag + lw x28, 0(x28) +__inf_loop: + j __inf_loop + + +.global flag +.data +flag: .word 0xFF diff --git a/verification/interrupts-exceptions/utility.c b/verification/interrupts-exceptions/utility.c new file mode 100644 index 000000000..d89404f7e --- /dev/null +++ b/verification/interrupts-exceptions/utility.c @@ -0,0 +1,25 @@ + +#include "utility.h" + +void print(char *string) { + volatile char *magic = (volatile char *)MAGIC_ADDR; + + for(int i = 0; string[i]; i++) { + (*magic) = string[i]; + } +} + +void put_uint32_hex(uint32_t x) { + char buf[10] = {0}; + + for(int i = 0; i < 8; i++) { + uint8_t value = (x & 0xF); + if(value >= 10) { + buf[7-i] = (value + 'A'); + } else { + buf[7-i] = (value + '0'); + } + x >>= 4; + } + print(buf); +} diff --git a/verification/interrupts-exceptions/utility.h b/verification/interrupts-exceptions/utility.h new file mode 100644 index 000000000..9229d8017 --- /dev/null +++ b/verification/interrupts-exceptions/utility.h @@ -0,0 +1,19 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + + #include + + #define MTIME_ADDR 0x10000 + #define MTIMEH_ADDR 0x10004 + #define MTIMECMP_ADDR 0x10008 + #define MTIMECMPH_ADDR 0x1000C + #define MSIP_ADDR 0x10010 + #define EXT_ADDR_SET 0x10014 + #define EXT_ADDR_CLEAR 0x10018 + #define MAGIC_ADDR 0x20000 + + + void print(char *string); + void put_uint32_hex(uint32_t hex); + +#endif diff --git a/verification/interrupts-exceptions/vectored.c b/verification/interrupts-exceptions/vectored.c new file mode 100644 index 000000000..ea2dff440 --- /dev/null +++ b/verification/interrupts-exceptions/vectored.c @@ -0,0 +1,98 @@ + +#include +#include "utility.h" + +extern volatile int flag; + +volatile uint32_t *mtime = (uint32_t *)MTIME_ADDR; +volatile uint32_t *mtimecmp = (uint32_t *)MTIMECMP_ADDR; +volatile uint32_t *mtimecmph = (uint32_t *)MTIMECMPH_ADDR; +volatile uint32_t *msip = (uint32_t *)MSIP_ADDR; +volatile uint32_t *ext_trigger = (uint32_t *)EXT_ADDR_SET; +volatile uint32_t *ext_clear = (uint32_t *)EXT_ADDR_CLEAR; + +// For this test: need to subtract 0xFE from flag to make flag = 1 +// Each handler should be called once. If not, flag will be wrong + +/* + * RISC-V Vector Layout + * 0 - reserved/exception (overlap of interrupt cause & exception cause) + * 1 - S-SW + * 2 - reserved + * 3 - M-SW + * 4 - reserved + * 5 - S-Timer + * 6 - reserved + * 7 - M-Timer + * 8 - reserved + * 9 - S-Ext + * 10- reserved + * 11- M-Ext + */ + +void __attribute__((interrupt)) m_ext_handler() { + flag -= 0xE; + (*ext_clear) = 0x1; // writing anything simulates clearing interrupt + uint32_t mie_value = 0x088; + asm volatile("csrw mie, %0" : : "r" (mie_value)); +} + +void __attribute__((interrupt)) m_timer_handler() { + flag -= 0xE0; + (*mtimecmph) = 0xFF; // setting mtimecmph makes a very large value +} + +void __attribute__((interrupt)) m_sw_handler() { + flag -= 0x10; + (*msip) = 0x0; // writing 0 clears this +} + +void __attribute__((interrupt)) default_handler() { + done(); // Go to done and fail test +} // should not end up here, this is a fail! + +// mtvec value MUST be aligned +// Note: .align 2 forces the jumps to be on multiple-of-4 boundaries here, +// which is required for vectored mode which computes the address as +// (mtvec.base + cause) x 4 +// If this test breaks, it may mean that the alignment is wrong, check the +// disassembly! +void __attribute__((naked)) __attribute__((aligned(4))) handler_dispatch() { + asm volatile(".align 2; j default_handler"); // 0 + asm volatile(".align 2; j default_handler"); // 1 + asm volatile(".align 2; j default_handler"); // 2 + asm volatile(".align 2; j m_sw_handler"); // 3 + asm volatile(".align 2; j default_handler"); // 4 + asm volatile(".align 2; j default_handler"); // 5 + asm volatile(".align 2; j default_handler"); // 6 + asm volatile(".align 2; j m_timer_handler"); // 7 + asm volatile(".align 2; j default_handler"); // 8 + asm volatile(".align 2; j default_handler"); // 9 + asm volatile(".align 2; j default_handler"); // 10 + asm volatile(".align 2; j m_ext_handler"); // 11 +} + + +int main() { + uint32_t mtvec_value = (uint32_t)handler_dispatch; + mtvec_value |= 1; // set vectored mode + uint32_t mie_value = 0x888; + uint32_t mstatus_value = 0x8; + + + // set mtimecmp away so interrupt doesn't fire immediately + *mtimecmp = 0xFF; + + // Setup interrupts + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + asm volatile("csrw mie, %0" : : "r" (mie_value)); + // Interrupts active + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + + (*msip) = 1; // trigger SW interrupt + (*ext_trigger) = 1; // trigger EXT interrupt + + while((*mtime) < 0xFF); + + return 0; +} diff --git a/verification/self-tests/RV32I/timer.S b/verification/self-tests/RV32I/timer.S deleted file mode 100644 index 0862fd255..000000000 --- a/verification/self-tests/RV32I/timer.S +++ /dev/null @@ -1,75 +0,0 @@ -/* -* Copyright 2016 Purdue University -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* -* Filename: basic_branch.S -* -* Created by: Jacob R. Stevens -* Email: steven69@purdue.edu -* Date Created: 07/01/2016 -* Description: Test of basic branching behavior -*/ - -#include "riscv_test.h" -#include "test_macros.h" - - -RVTEST_RV32U - - -RVTEST_CODE_BEGIN - -.equ mtime, 0x80050004 -.equ mtimecmp, 0x8005000C - -main: - # Define trap vector address - la t0, mtvec_handler - csrw mtvec, t0 - - addi x6, x0, 0xFF - addi x7, x0, 0xFF - addi x3, x0, 0x1 - slli x3, x3, 3 - csrw mstatus, x3 - li x1, mtime - lw x2, 0(x1) - addi x2, x2, 50 - li x1, mtimecmp - sw x2, 0(x1) - ori x3, x0, 0x80 - csrw mie, x3 -idle_loop: - beq x6, x7, idle_loop - RVTEST_PASS - -.align 2 -mtvec_handler: - # Write to mtimecmp using the zero register - # Check that nothing happened to interrupt - # Actually write to mtimecmp - # Check that the interrupt cleared - # Change sigil to show interrupt was entered - ori x6, x0, 0x0F - # Return - mret - -RVTEST_CODE_END - -.data -RVTEST_DATA_BEGIN - TEST_DATA -RVTEST_DATA_END - From 1a203c87f65d7af73ca9a925b20c6ea0239a9f76 Mon Sep 17 00:00:00 2001 From: Cole Nelson <35197931+cole-nelson@users.noreply.github.com> Date: Tue, 18 Oct 2022 14:08:39 -0400 Subject: [PATCH 15/47] Fusesoc (#20) * Initial support for FuseSoC in RISCVBusiness Co-authored-by: Mitch Co-authored-by: Hadi Ahmed --- .gitignore | 5 + .rules.verible_lint | 3 + Makefile | 39 +- README.md | 41 +- RISCVBusiness.core | 135 +++ dev.yml | 3 +- run_tests_verilator.py | 2 +- scripts/config_core.py | 2 +- scripts/pre-commit | 8 + setup.sh | 41 + .../Floating_Point_Unit/source/ADD_step1.sv | 71 +- .../Floating_Point_Unit/source/ADD_step2.sv | 97 +- .../Floating_Point_Unit/source/ADD_step3.sv | 144 ++- .../source/FPU_top_level.sv | 478 +++++---- .../Floating_Point_Unit/source/MUL_step1.sv | 45 +- .../Floating_Point_Unit/source/MUL_step2.sv | 47 +- .../Floating_Point_Unit/source/adder_26b.sv | 46 +- .../Floating_Point_Unit/source/adder_8b.sv | 40 +- .../Floating_Point_Unit/source/int_compare.sv | 40 +- .../Floating_Point_Unit/source/left_shift.sv | 147 ++- .../Floating_Point_Unit/source/max_select.sv | 30 +- .../Floating_Point_Unit/source/mul_26b.sv | 31 +- .../Floating_Point_Unit/source/right_shift.sv | 72 +- .../Floating_Point_Unit/source/rounder.sv | 82 +- .../Floating_Point_Unit/source/s_to_u.sv | 30 +- .../Floating_Point_Unit/source/subtract.sv | 31 +- .../Floating_Point_Unit/source/u_to_s.sv | 22 +- source_code/RV32E/rv32e_reg_file.sv | 57 +- source_code/RV32E/rv32e_wrapper.sv | 8 +- .../branch_predictor_wrapper.sv | 23 +- .../nottaken_predictor/nottaken_predictor.sv | 17 +- source_code/branch_predictors/wscript | 8 - source_code/bus_bridges/ahb.sv | 140 ++- source_code/bus_bridges/apb.sv | 78 ++ .../bus_bridges/generic_nonpipeline.sv | 70 +- source_code/caches/caches.core | 20 + source_code/caches/caches_wrapper.sv | 35 +- .../direct_mapped_tpf_cache.sv | 950 +++++++++--------- .../caches/pass_through/pass_through_cache.sv | 37 +- source_code/caches/separate_caches.sv | 127 +-- source_code/caches/wscript | 10 - source_code/fpga/RISCVBusiness_fpga.sv | 2 +- source_code/include/ahb_if.vh | 40 - source_code/include/include.core | 37 + source_code/include/priv_1_12_internal_if.vh | 2 +- source_code/packages/alu_types_pkg.sv | 38 +- .../packages/machine_mode_types_1_11_pkg.sv | 424 ++++---- .../packages/machine_mode_types_1_12_pkg.sv | 52 +- .../packages/machine_mode_types_1_7_pkg.sv | 446 ++++---- .../packages/machine_mode_types_pkg.sv | 456 ++++----- source_code/packages/risc_mgmt/crc32_pkg.sv | 30 +- source_code/packages/risc_mgmt/rv32m_pkg.sv | 68 +- .../packages/risc_mgmt/template_pkg.sv | 26 +- source_code/packages/risc_mgmt/test_pkg.sv | 88 +- source_code/packages/riscv_packages.core | 30 + source_code/packages/rv32i_types_pkg.sv | 368 +++---- source_code/packages/wscript | 8 - source_code/pipelines/pipeline_wrapper.sv | 35 +- source_code/pipelines/tspp/tspp.core | 29 + source_code/pipelines/tspp/tspp.sv | 45 +- .../pipelines/tspp/tspp_execute_stage.sv | 678 ++++++------- .../pipelines/tspp/tspp_fetch_stage.sv | 176 ++-- .../pipelines/tspp/tspp_hazard_unit.sv | 154 +-- source_code/pipelines/wscript | 8 - source_code/privs/priv.core | 28 + .../privs/priv_1_11/priv_1_11_block.sv | 152 +-- .../privs/priv_1_11/priv_1_11_control.sv | 311 +++--- .../privs/priv_1_11/priv_1_11_csr_rfile.sv | 40 +- .../priv_1_11/priv_1_11_pipeline_control.sv | 50 +- .../privs/priv_1_12/priv_1_12_block.sv | 4 +- source_code/privs/priv_1_12/priv_1_12_csr.sv | 40 +- .../priv_1_12/priv_1_12_int_ex_handler.sv | 32 +- .../privs/priv_1_12/priv_1_12_pipe_control.sv | 13 +- source_code/privs/priv_wrapper.sv | 9 +- source_code/privs/wscript | 8 - source_code/ram/config_ram_wrapper.sv | 79 +- source_code/ram/ram_sim_model.sv | 302 +++--- source_code/ram/ram_wrapper.sv | 61 +- .../risc_mgmt/extensions/crc32/crc32.sv | 73 +- .../extensions/crc32/crc32_decode.sv | 53 +- .../extensions/crc32/crc32_execute.sv | 49 +- .../extensions/crc32/crc32_memory.sv | 31 +- .../extensions/rv32m/carry_save_adder.sv | 41 +- .../extensions/rv32m/flex_counter_mul.sv | 81 +- .../risc_mgmt/extensions/rv32m/full_adder.sv | 17 +- .../risc_mgmt/extensions/rv32m/pp_mul32.sv | 551 ++++++---- .../extensions/rv32m/radix4_divider.sv | 265 ++--- .../extensions/rv32m/rv32m_decode.sv | 60 +- .../extensions/rv32m/rv32m_execute.sv | 259 ++--- .../extensions/rv32m/rv32m_memory.sv | 31 +- .../extensions/rv32m/shift_add_multiplier.sv | 99 +- .../rv32m/shift_test_restore_divider.sv | 125 ++- .../extensions/template/template_decode.sv | 37 +- .../extensions/template/template_execute.sv | 33 +- .../extensions/template/template_memory.sv | 31 +- source_code/risc_mgmt/extensions/wscript | 14 - source_code/risc_mgmt/risc_mgmt.core | 33 + source_code/risc_mgmt/risc_mgmt_wrapper.sv | 13 +- source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv | 273 +++-- source_code/risc_mgmt/wscript | 10 - source_code/rv32c/decompressor.sv | 232 +++-- source_code/rv32c/fetch_buffer.sv | 39 +- source_code/rv32c/rv32c.core | 18 + source_code/rv32c/rv32c_disabled.sv | 26 +- source_code/rv32c/rv32c_enabled.sv | 67 +- source_code/rv32c/rv32c_wrapper.sv | 23 +- .../sparce/sparce_disabled/sparce_disabled.sv | 25 +- .../sparce/sparce_enabled/sparce_cfid.sv | 28 +- .../sparce/sparce_enabled/sparce_enabled.sv | 71 +- .../sparce/sparce_enabled/sparce_psru.sv | 42 +- .../sparce_enabled/sparce_sasa_table.sv | 14 +- .../sparce/sparce_enabled/sparce_sprf.sv | 86 +- .../sparce/sparce_enabled/sparce_svc.sv | 16 +- source_code/sparce/sparce_wrapper.sv | 31 +- source_code/sparce/wscript | 8 - source_code/standard_core/RISCVBusiness.sv | 305 +++--- source_code/standard_core/alu.sv | 89 +- source_code/standard_core/branch_res.sv | 103 +- source_code/standard_core/control_unit.sv | 390 ++++--- source_code/standard_core/dmem_extender.sv | 106 +- source_code/standard_core/endian_swapper.sv | 31 +- source_code/standard_core/jump_calc.sv | 23 +- .../standard_core/memory_controller.sv | 372 ++++--- source_code/standard_core/rv32i_reg_file.sv | 33 +- source_code/standard_core/standard_core.core | 33 + source_code/standard_core/top_core.sv | 110 +- source_code/tb/tb_RISCVBusiness_self_test.sv | 43 +- source_code/trackers/branch_tracker.sv | 146 +-- source_code/trackers/cpu_tracker.core | 21 + source_code/trackers/cpu_tracker.sv | 462 ++++----- source_code/wscript | 143 --- wscript | 31 - 132 files changed, 6753 insertions(+), 6274 deletions(-) create mode 100644 .rules.verible_lint create mode 100644 RISCVBusiness.core create mode 100755 scripts/pre-commit create mode 100755 setup.sh delete mode 100644 source_code/branch_predictors/wscript create mode 100644 source_code/bus_bridges/apb.sv create mode 100644 source_code/caches/caches.core delete mode 100644 source_code/caches/wscript delete mode 100644 source_code/include/ahb_if.vh create mode 100644 source_code/include/include.core create mode 100644 source_code/packages/riscv_packages.core delete mode 100644 source_code/packages/wscript create mode 100644 source_code/pipelines/tspp/tspp.core delete mode 100644 source_code/pipelines/wscript create mode 100644 source_code/privs/priv.core delete mode 100644 source_code/privs/wscript delete mode 100644 source_code/risc_mgmt/extensions/wscript create mode 100644 source_code/risc_mgmt/risc_mgmt.core delete mode 100644 source_code/risc_mgmt/wscript create mode 100644 source_code/rv32c/rv32c.core delete mode 100644 source_code/sparce/wscript create mode 100644 source_code/standard_core/standard_core.core create mode 100644 source_code/trackers/cpu_tracker.core delete mode 100644 source_code/wscript delete mode 100644 wscript diff --git a/.gitignore b/.gitignore index 0f714ce59..ac457f2e4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build/ sim_out/ obj_dir/ +rvb_out/ # ignore memory files from test cases *.hex meminit.ver @@ -61,3 +62,7 @@ xrun.key waves.shm/ open_files.sh + +# fusesoc +fusesoc_libraries +fusesoc.conf diff --git a/.rules.verible_lint b/.rules.verible_lint new file mode 100644 index 000000000..98a2d74ea --- /dev/null +++ b/.rules.verible_lint @@ -0,0 +1,3 @@ +parameter-name-style=localparam_style:CamelCase|ALL_CAPS +-explicit-function-lifetime +-explicit-task-lifetime diff --git a/Makefile b/Makefile index b28b56e20..421a6482d 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ PREDICTOR_FILES := $(BRANCH_PREDICT)/branch_predictor_wrapper.sv $(BRANCH_PREDIC PRIV_FILES := $(PRIVS)/priv_wrapper.sv $(PRIVS)/priv_1_12/priv_1_12_block.sv $(PRIVS)/priv_1_12/priv_1_12_int_ex_handler.sv $(PRIVS)/priv_1_12/priv_1_12_csr.sv $(PRIVS)/priv_1_12/priv_1_12_pipe_control.sv CACHE_FILES := $(CACHES)/caches_wrapper.sv $(CACHES)/pass_through/pass_through_cache.sv $(CACHES)/direct_mapped_tpf/direct_mapped_tpf_cache.sv $(CACHES)/separate_caches.sv SPARCE_FILES := $(SPARCE)/sparce_wrapper.sv $(SPARCE)/sparce_disabled/sparce_disabled.sv $(SPARCE)/sparce_enabled/sparce_cfid.sv $(SPARCE)/sparce_enabled/sparce_enabled.sv $(SPARCE)/sparce_enabled/sparce_psru.sv $(SPARCE)/sparce_enabled/sparce_sasa_table.sv $(SPARCE)/sparce_enabled/sparce_sprf.sv $(SPARCE)/sparce_enabled/sparce_svc.sv -RISCV_BUS_FILES := $(RISCV_BUS)/generic_nonpipeline.sv #$(RISCV_BUS)/ahb.sv +RISCV_BUS_FILES := $(RISCV_BUS)/generic_nonpipeline.sv $(RISCV_BUS)/ahb.sv TRACKER_FILES := $(RISCV)/trackers/cpu_tracker.sv $(RISCV)/trackers/branch_tracker.sv COMPONENT_FILES_SV := $(CORE_PKG_FILES) $(RISC_MGMT_FILES) $(RISC_EXT_FILES) $(CORE_FILES) $(RV32C_FILES) $(PIPELINE_FILES) $(SPARCE_FILES) $(PREDICTOR_FILES) $(PRIV_FILES) $(CACHE_FILES) $(RISCV_BUS_FILES) $(TRACKER_FILES) @@ -33,25 +33,44 @@ HEADER_FILES := -I$(RISCV)/include define USAGE -@echo "-----------------------------------------" +@echo "----------------------------------------------------------------------" @echo " Build Targets:" @echo " config: config core with example.yml" -@echo " verilate: Invoke verilator" -@echo "-----------------------------------------" +@echo " verilate: Invoke 'FuseSoC run --build' to build Verilator target" +@echo " xcelium: Invoke 'FuseSoC run --build' to build Xcelium target" +@echo " clean: Remove build directories" +@echo " veryclean: Remove fusesoc libraries & build directories" +@echo "----------------------------------------------------------------------" endef -.phony: default clean +.phony: default clean config verilate xcelium default: $(USAGE) config: - python3 scripts/config_core.py example.yml + @echo "----------------------" + @echo " Running config_core" + @echo "----------------------" + @python3 scripts/config_core.py example.yml -verilate: - verilator -Wno-UNOPTFLAT -Wno-SYMRSVDWORD -cc -Wno-lint --report-unoptflat --trace-fst --trace-structs --top-module top_core $(HEADER_FILES) $(COMPONENT_FILES_SV) --exe tb_core.cc - $(MAKE) -C obj_dir -f Vtop_core.mk -j `nproc` +verilate: config + @fusesoc --cores-root . run --setup --build --build-root rvb_out --target sim --tool verilator socet:riscv:RISCVBusiness + @echo "------------------------------------------------------------------" + @echo "Build finished, you can run with 'fusesoc run', or by navigating" + @echo "to the build directory created by FuseSoC and using the Makefile there." + @echo "------------------------------------------------------------------" + +xcelium: config + @fusesoc --cores-root . run --setup --build --build-root rvb_out --target sim --tool xcelium socet:riscv:RISCVBusiness + @echo "Build finished, you can run with 'fusesoc run', or by navigating" + @echo "to the build directory created by FuseSoC and using the Makefile there." clean: - rm -rf obj_dir + rm -rf build + rm -rf rvb_out + +veryclean: + rm -rf fusesoc_libraries + rm fusesoc.conf diff --git a/README.md b/README.md index e7e8187a9..24bf36388 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,43 @@ Design documents and project information for the RISC-V Business project can be [RISCV-Business Documentation](https://wiki.itap.purdue.edu/display/RISC/RISCV-Business) **Current User-Level ISA Spec :** v2.1 -**Current Privileged ISA Spec :** v1.11 +**Current Privileged ISA Spec :** v1.12 # Getting Started Getting started with RISCV Business consists of three steps: setting up the RISCV tool-chain, setting up the WAF build system, and finally interacting with the RISCV Business project itself. +## Fusesoc +This project uses the [Fusesoc](http://fusesoc.net/) build system. Use the following commands to get started... + +```bash +# install project dependencies & +# setup git pre-commit hook +./setup.sh + +# For ease of use, use the makefile to run FuseSoC tests +make # shows all build targets available + +# configure the RISC-V core +make config +# or python3 scripts/config_core.py .yml +# if you want to use a config other than example.yml + +make verilate # build with Verilator, or... +make xcelium # build with Xcelium +``` + +> Congrats! All dependencies are now set up. Now you can run simulations/tests: + + +```bash +# Run ISA tests +run_tests_verilator.py + +# Run specify binary on Verliator core simulation +./rvb_out/sim-verilator/Vtop_core meminit.bin +``` + ## Generating RISC-V tool-chain Refer to the following link for instructions on installing the RISC-V software tools: @@ -36,14 +67,6 @@ Then run after setting the environment variable "RISCV" to your install location ./build.sh ~~~ -## Installing the build environment - -RISCVBusiness uses SoCFoundationFlow, built off of the waf build system. The following repository contains the source for waf: - -[SoCFoundationFlow](https://github.com/mattaw/SoCFoundationFlow) - -Refer to SoCFoundationFlow for installation instructions. - ## Setup and Run RISCV Business First, clone the repository diff --git a/RISCVBusiness.core b/RISCVBusiness.core new file mode 100644 index 000000000..b2309cb4d --- /dev/null +++ b/RISCVBusiness.core @@ -0,0 +1,135 @@ +CAPI=2: +name: socet:riscv:RISCVBusiness:0.1.0 +description: RISC-V Core for AFTx series + +filesets: + rtl: + depend: + - "socet:bus-components:bus_protocol_if" + - "socet:bus-components:ahb_if" + - "socet:bus-components:apb_if" + - "socet:riscv:packages" + - "socet:riscv:priv" + - "socet:riscv:caches" + - "socet:riscv:risc_mgmt" + - "socet:riscv:riscv_standard" + - "socet:riscv:riscv_include" + - "socet:riscv:rv32c" + files: + - source_code/branch_predictors/branch_predictor_wrapper.sv + - source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv + - source_code/bus_bridges/generic_nonpipeline.sv + - source_code/pipelines/tspp/tspp_hazard_unit.sv + - source_code/pipelines/tspp/tspp_fetch_stage.sv + - source_code/pipelines/tspp/tspp_execute_stage.sv + - source_code/sparce/sparce_disabled/sparce_disabled.sv + - source_code/RV32E/rv32e_reg_file.sv + - source_code/RV32E/rv32e_wrapper.sv + file_type: systemVerilogSource + + trackers: + files: + - source_code/trackers/branch_tracker.sv + - source_code/trackers/cpu_tracker.sv + file_type: systemVerilogSource + + buses: + files: + - "!tool_verilator? (source_code/bus_bridges/ahb.sv)" + - "!tool_verilator? (source_code/bus_bridges/apb.sv)" + file_type: systemVerilogSource + + fpga_wrapper: + files: + - source_code/fpga/RISCVBusiness_fpga.sv + - source_code/ram/ram_sim_model.sv + - source_code/ram/ram_wrapper.sv + file_type: systemVerilogSource + + verilator_tb: + files: + - tb_core.cc : {fileType: cppSource} + file_type: systemVerilogSource + + tb: + files: + - source_code/tb/tb_RISCVBusiness_self_test.sv + - source_code/ram/config_ram_wrapper.sv + - source_code/ram/ram_sim_model.sv + - source_code/ram/ram_wrapper.sv + file_type: systemVerilogSource + + +targets: + default: &default + filesets: + - rtl + - buses + toplevel: RISCVBusiness + + sim: + <<: *default + description: Simulate w/TB + default_tool: verilator + filesets_append: + - trackers + - "tool_verilator? (verilator_tb)" + - "!tool_verilator? (tb)" + toplevel: + - "tool_verilator? (top_core)" + - "!tool_verilator? (tb_RISCVBusiness_self_test)" + tools: + xcelium: + xrun_options: + - +xmtimescale+1ns/100ps + modelsim: + vsim_options: + - -vopt + - -voptargs='+acc' + - -t ps + + verilator: + verilator_options: ["-Wno-UNOPTFLAT", "-Wno-SYMRSVDWORD", "-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] + + fpga: + <<: *default + filesets_append: + - buses + - fpga_wrapper + description: FPGA Synthesis + default_tool: quartus + parameters: + - SYNTHESIS=true + toplevel: top_core + tools: + quartus: + family: Cyclone IV E + device: EP4CE115F29C7 + + + lint: + filesets: + - rtl + - trackers + description: Linting + default_tool: veriblelint + toplevel: top_core + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search', '--waiver_files=riscv.waiver'] + + format: + filesets: + - rtl + description: Linting + default_tool: veribleformat + toplevel: top_core + tools: + veribleformat: + verible_format_args: ['--indentation_spaces=4', '--inplace'] + +parameters: + SYNTHESIS: + description: Set for tools performing synthesis + datatype: bool + paramtype: vlogdefine diff --git a/dev.yml b/dev.yml index 02895a91a..e73c60907 100644 --- a/dev.yml +++ b/dev.yml @@ -14,7 +14,7 @@ microarch_params: # Bus configurations bus_endianness : "big" - bus_interface_type : "ahb_if" + bus_interface_type : "generic_bus_if" # Sparisty Optimizations sparce_enabled : "disabled" @@ -31,5 +31,6 @@ microarch_params: # RISC-MGMT Extension Configuration risc_mgmt_params: standard_extensions: + - name: "rv32m" nonstandard_extensions: diff --git a/run_tests_verilator.py b/run_tests_verilator.py index c007417d6..28073aa56 100755 --- a/run_tests_verilator.py +++ b/run_tests_verilator.py @@ -217,7 +217,7 @@ def compile_asm(filepath: Type[pathlib.Path], outpath: Type[pathlib.Path],\ def run_sim(top_level: str, logger: Type[logging.Logger]) -> None: # TODO: Assuming simulator already built - cmd_arr = ['./obj_dir/Vtop_core', 'meminit.bin'] + cmd_arr = ['./rvb_out/sim-verilator/Vtop_core', 'meminit.bin'] res = subprocess.run(cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) #config_cmd_arr = ["waf", "configure", "--top_level=" + top_level] diff --git a/scripts/config_core.py b/scripts/config_core.py index 3654bf08d..a0f92ef21 100755 --- a/scripts/config_core.py +++ b/scripts/config_core.py @@ -44,7 +44,7 @@ 'icache_type' : ['pass_through', 'direct_mapped_tpf'], # Bus Configurations 'bus_endianness' : ['big', 'little'], - 'bus_interface_type' : ['ahb_if', 'generic_bus_if'], + 'bus_interface_type' : ['ahb_if', 'generic_bus_if', 'apb_if'], # Sparisty Optimizations 'sparce_enabled' : [ 'enabled', 'disabled' ], # RV32C diff --git a/scripts/pre-commit b/scripts/pre-commit new file mode 100755 index 000000000..9252ca88e --- /dev/null +++ b/scripts/pre-commit @@ -0,0 +1,8 @@ +#! /bin/sh +echo "Pre-Commit: Running Lint" +fusesoc --cores-root . run --no-export --target lint socet:aft:gpio + +if [ $? -ne 0 ] +then + exit 1 +fi diff --git a/setup.sh b/setup.sh new file mode 100755 index 000000000..344e45078 --- /dev/null +++ b/setup.sh @@ -0,0 +1,41 @@ +#! /bin/sh + +RED='\033[0;31m' +BLUE='\033[0;34m' +GREEN='\033[0;32m' +NC='\033[0m' # No Color + +info_print() { + echo -e "$BLUE$1$NC" +} + +error_print() { + echo -e "$RED$1$NC" +} + +success_print() { + echo -e "$GREEN$1$NC" +} + +echo "Initializing Repository..." + +if [ -d "./fusesoc_libraries" ] ; then + rm -rf ./fusesoc_libraries # clean old fusesoc libs + rm ./fusesoc.conf +fi + +info_print "Installing Digital Libraries" +if ! fusesoc library add digital-lib git@github.com:Purdue-SoCET/digital-lib.git ; then + error_print "Failed to fetch digital-lib: ensure that ssh key is set up for repository permissions" + exit 1 +fi + +if ! fusesoc library add bus-components git@github.com:Purdue-SoCET/bus-components.git ; then + error_print "Failed to fetch bus-components: ensure that ssh key is set up for repository permissions" + exit 1 +fi + +#info_print "Installing pre-commit hook" +#cp ./scripts/pre-commit ./.git/hooks/ + +success_print "Initialization Complete!" diff --git a/source_code/Floating_Point_Unit/source/ADD_step1.sv b/source_code/Floating_Point_Unit/source/ADD_step1.sv index 57629ba02..f6b0a90d4 100644 --- a/source_code/Floating_Point_Unit/source/ADD_step1.sv +++ b/source_code/Floating_Point_Unit/source/ADD_step1.sv @@ -13,43 +13,42 @@ // sign_not_shifted - sign of the floating point that does not get shifted // frac_not_shifted - fraction of the floating point that does not get shifted // exp_max - max exponent of the two given floating points -module ADD_step1 - ( - input [31:0] floating_point1_in, - input [31:0] floating_point2_in, - output sign_shifted, - output [25:0] frac_shifted, - output sign_not_shifted, - output [25:0] frac_not_shifted, - output [7:0] exp_max - ); +module ADD_step1 ( + input [31:0] floating_point1_in, + input [31:0] floating_point2_in, + output sign_shifted, + output [25:0] frac_shifted, + output sign_not_shifted, + output [25:0] frac_not_shifted, + output [7:0] exp_max +); - reg [7:0] unsigned_exp_diff; - reg cmp_out; //exp1 >= exp2 -> cmp_out == 0 - //exp1 < exp2 -> cmp_out == 1 - wire [31:0] floating_point_shift; - wire [31:0] floating_point_not_shift; - reg [31:0] shifted_floating_point; - - int_compare cmp_exponents ( - .exp1(floating_point1_in[30:23]), - .exp2(floating_point2_in[30:23]), - .u_diff(unsigned_exp_diff), - .cmp_out(cmp_out) - ); - assign floating_point_shift = cmp_out ? floating_point1_in : floating_point2_in; - assign floating_point_not_shift = cmp_out ? floating_point2_in : floating_point1_in; - assign exp_max = cmp_out ? floating_point2_in[30:23] : floating_point1_in[30:23]; - - right_shift shift_frac_with_smaller_exp ( - .fraction({1'b1, floating_point_shift[22:0], 2'b0}), - .shift_amount(unsigned_exp_diff), - .result(frac_shifted) - ); + reg [7:0] unsigned_exp_diff; + reg cmp_out; //exp1 >= exp2 -> cmp_out == 0 + //exp1 < exp2 -> cmp_out == 1 + wire [31:0] floating_point_shift; + wire [31:0] floating_point_not_shift; + reg [31:0] shifted_floating_point; + + int_compare cmp_exponents ( + .exp1(floating_point1_in[30:23]), + .exp2(floating_point2_in[30:23]), + .u_diff(unsigned_exp_diff), + .cmp_out(cmp_out) + ); + assign floating_point_shift = cmp_out ? floating_point1_in : floating_point2_in; + assign floating_point_not_shift = cmp_out ? floating_point2_in : floating_point1_in; + assign exp_max = cmp_out ? floating_point2_in[30:23] : floating_point1_in[30:23]; + + right_shift shift_frac_with_smaller_exp ( + .fraction({1'b1, floating_point_shift[22:0], 2'b0}), + .shift_amount(unsigned_exp_diff), + .result(frac_shifted) + ); + + assign frac_not_shifted = {1'b1, floating_point_not_shift[22:0], 2'b0}; + assign sign_not_shifted = floating_point_not_shift[31]; + assign sign_shifted = floating_point_shift[31]; - assign frac_not_shifted = {1'b1, floating_point_not_shift[22:0], 2'b0}; - assign sign_not_shifted = floating_point_not_shift[31]; - assign sign_shifted = floating_point_shift[31]; - endmodule diff --git a/source_code/Floating_Point_Unit/source/ADD_step2.sv b/source_code/Floating_Point_Unit/source/ADD_step2.sv index d7ac08b5d..f5ae248b4 100644 --- a/source_code/Floating_Point_Unit/source/ADD_step2.sv +++ b/source_code/Floating_Point_Unit/source/ADD_step2.sv @@ -10,60 +10,59 @@ // sign1/2 - signs of fractions to be added // exp_max_in - max exponent from step1 (if the sum is zero the exponent is set to zero) //Outputs: -// sign_out - sign of the result +// sign_out - sign of the result // sum - magnitude of the result regardless of any carry out // carry_out - signal if there is an oveflow from the addition // exp_max_out - if sum is non-zero, this is equal to exp_max_in -module ADD_step2 - ( - input [25:0] frac1, - input sign1, - input [25:0] frac2, - input sign2, - input [7:0] exp_max_in, // - output sign_out, - output [25:0] sum, - output carry_out, - output reg [7:0] exp_max_out// - ); +module ADD_step2 ( + input [25:0] frac1, + input sign1, + input [25:0] frac2, + input sign2, + input [ 7:0] exp_max_in, // + output sign_out, + output [25:0] sum, + output carry_out, + output reg [ 7:0] exp_max_out // +); - reg [26:0] frac1_signed; - reg [26:0] frac2_signed; - reg [26:0] sum_signed; - - always_comb begin : exp_max_assignment - if(sum_signed == 0) exp_max_out = 8'b00000000; - else exp_max_out = exp_max_in; - end - + reg [26:0] frac1_signed; + reg [26:0] frac2_signed; + reg [26:0] sum_signed; + + always_comb begin : exp_max_assignment + if (sum_signed == 0) exp_max_out = 8'b00000000; + else exp_max_out = exp_max_in; + end + + + + u_to_s change_to_signed1 ( + .sign(sign1), + .frac_unsigned(frac1), + .frac_signed(frac1_signed) + ); + + u_to_s change_to_signed2 ( + .sign(sign2), + .frac_unsigned(frac2), + .frac_signed(frac2_signed) + ); + + adder_26b add_signed_fracs ( + .frac1(frac1_signed), + .frac2(frac2_signed), + .sum (sum_signed), + .ovf (carry_out) + ); + + s_to_u change_to_unsigned ( + .frac_signed(sum_signed), + .sign(sign_out), + .frac_unsigned(sum) + ); - - u_to_s change_to_signed1( - .sign(sign1), - .frac_unsigned(frac1), - .frac_signed(frac1_signed) - ); - - u_to_s change_to_signed2( - .sign(sign2), - .frac_unsigned(frac2), - .frac_signed(frac2_signed) - ); - - adder_26b add_signed_fracs( - .frac1(frac1_signed), - .frac2(frac2_signed), - .sum(sum_signed), - .ovf(carry_out) - ); - s_to_u change_to_unsigned( - .frac_signed(sum_signed), - .sign(sign_out), - .frac_unsigned(sum) - ); - - endmodule - + diff --git a/source_code/Floating_Point_Unit/source/ADD_step3.sv b/source_code/Floating_Point_Unit/source/ADD_step3.sv index 809046a5c..4be3c96ec 100644 --- a/source_code/Floating_Point_Unit/source/ADD_step3.sv +++ b/source_code/Floating_Point_Unit/source/ADD_step3.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/23/18 // -//Module Summary: +//Module Summary: // Third step of addition operation in three-step pipeline. // Rounds result based on rounding mode (frm) and left shifts fraction if needed // @@ -14,85 +14,83 @@ //Outputs: // floating_point_out - final floating point -module ADD_step3 - ( - input ovf_in, - input unf_in, - input dz, // divide by zero flag - input inv, - input [2:0] frm, - input [7:0] exponent_max_in, - input sign_in, - input [25:0] frac_in, - input carry_out, - output [31:0] floating_point_out, - output [4:0] flags - ); +module ADD_step3 ( + input ovf_in, + input unf_in, + input dz, // divide by zero flag + input inv, + input [2:0] frm, + input [7:0] exponent_max_in, + input sign_in, + input [25:0] frac_in, + input carry_out, + output [31:0] floating_point_out, + output [4:0] flags +); - wire inexact; - wire sign; - wire [7:0] exponent; - wire [22:0] frac; - - assign {sign, exponent, frac} = floating_point_out; - - reg [7:0] exp_minus_shift_amount; - reg [25:0] shifted_frac; - reg [7:0] shifted_amount; - reg [7:0] exp_out; - reg ovf; - reg unf; - + wire inexact; + wire sign; + wire [ 7:0] exponent; + wire [22:0] frac; - left_shift shift_left ( - .fraction(frac_in), - .result(shifted_frac), - .shifted_amount(shifted_amount) - ); - - subtract SUB ( - .exp1(exponent_max_in), - .shifted_amount(shifted_amount), - .result(exp_minus_shift_amount) - ); + assign {sign, exponent, frac} = floating_point_out; - - reg [24:0] round_this; - - always_comb begin - ovf = 0; - unf = 0; - if(carry_out == 1) begin - round_this = frac_in[25:1]; - exp_out = exponent_max_in + 1; - if((exponent_max_in == 8'b11111110) && (~unf_in)) ovf = 1; - end - else begin - round_this = shifted_frac[24:0]; - exp_out = exp_minus_shift_amount; - if(({1'b0, exponent_max_in} < shifted_amount) && (~ovf_in)) unf = 1; - end - end + reg [ 7:0] exp_minus_shift_amount; + reg [25:0] shifted_frac; + reg [ 7:0] shifted_amount; + reg [ 7:0] exp_out; + reg ovf; + reg unf; - reg [31:0] round_out; - - rounder ROUND ( - .frm(frm), - .sign(sign_in), - .exp_in(exp_out), - .fraction(round_this), - .round_out(round_out), - .rounded(round_flag) - ); - - assign inexact = ovf_in | ovf | unf_in | unf | round_flag; - assign flags = {inv, dz, (ovf | ovf_in), (unf | unf_in), inexact}; - assign floating_point_out[31] = round_out[31]; - assign floating_point_out[30:0] = inv ? 31'b1111111101111111111111111111111 : + + left_shift shift_left ( + .fraction(frac_in), + .result(shifted_frac), + .shifted_amount(shifted_amount) + ); + + subtract SUB ( + .exp1(exponent_max_in), + .shifted_amount(shifted_amount), + .result(exp_minus_shift_amount) + ); + + + reg [24:0] round_this; + + always_comb begin + ovf = 0; + unf = 0; + if (carry_out == 1) begin + round_this = frac_in[25:1]; + exp_out = exponent_max_in + 1; + if ((exponent_max_in == 8'b11111110) && (~unf_in)) ovf = 1; + end else begin + round_this = shifted_frac[24:0]; + exp_out = exp_minus_shift_amount; + if (({1'b0, exponent_max_in} < shifted_amount) && (~ovf_in)) unf = 1; + end + end + + reg [31:0] round_out; + + rounder ROUND ( + .frm(frm), + .sign(sign_in), + .exp_in(exp_out), + .fraction(round_this), + .round_out(round_out), + .rounded(round_flag) + ); + + assign inexact = ovf_in | ovf | unf_in | unf | round_flag; + assign flags = {inv, dz, (ovf | ovf_in), (unf | unf_in), inexact}; + assign floating_point_out[31] = round_out[31]; + assign floating_point_out[30:0] = inv ? 31'b1111111101111111111111111111111 : ovf_in ? 31'b1111111100000000000000000000000 : ovf ? 31'b1111111100000000000000000000000 : unf_in ? 31'b0000000000000000000000000000000 : unf ? 31'b0000000000000000000000000000000 : round_out[30:0]; - + endmodule diff --git a/source_code/Floating_Point_Unit/source/FPU_top_level.sv b/source_code/Floating_Point_Unit/source/FPU_top_level.sv index 8db454c7b..9a6694798 100644 --- a/source_code/Floating_Point_Unit/source/FPU_top_level.sv +++ b/source_code/Floating_Point_Unit/source/FPU_top_level.sv @@ -14,260 +14,258 @@ // frm - rounding mode // funct7 - 7 bit operation code //Outputs: -// floating_point_out - result of operation +// floating_point_out - result of operation // flags - 5 error flags (overflow, underflow, divide by zero, inexact result, invalid operation) -module FPU_top_level -( - input clk, - input nrst, - input [31:0] floating_point1, - input [31:0] floating_point2, - input [2:0] frm, - input [6:0] funct7, - output [31:0] floating_point_out, - output [4:0] flags - ); - - - - reg [2:0] frm2; - reg [2:0] frm3; - reg [6:0] funct7_2; - reg [6:0] funct7_3; - - //funct7 definitions - localparam ADD = 7'b0100000; - localparam MUL = 7'b0000010; - - // ADD step1 outputs -> step2 inputs - //reg nxt_sign_shifted; - reg sign_shifted; - //reg [25:0] nxt_frac_shifted; - reg [25:0] frac_shifted; - //reg nxt_sign_not_shifted; - reg sign_not_shifted; - //reg [25:0] nxt_frac_not_shifted; - reg [25:0] frac_not_shifted; - //reg [7:0] nxt_exp_max; - reg [7:0] exp_max; - - // MUL step1 outputs -> step2 inputs - reg mul_sign1; - reg mul_sign2; - reg [7:0] mul_exp1; - reg [7:0] mul_exp2; - reg [25:0] product; - reg mul_carry_out; - - reg [61:0] step1_to_step2; - reg [61:0] nxt_step1_to_step2; - - - // ADD step2 outputs -> step3 inputs - reg add_sign_out; - //reg nxt_sign_out; - reg [25:0] add_sum; +module FPU_top_level ( + input clk, + input nrst, + input [31:0] floating_point1, + input [31:0] floating_point2, + input [ 2:0] frm, + input [ 6:0] funct7, + output [31:0] floating_point_out, + output [ 4:0] flags +); + + + + reg [2:0] frm2; + reg [2:0] frm3; + reg [6:0] funct7_2; + reg [6:0] funct7_3; + + //funct7 definitions + localparam ADD = 7'b0100000; + localparam MUL = 7'b0000010; + + // ADD step1 outputs -> step2 inputs + //reg nxt_sign_shifted; + reg sign_shifted; + //reg [25:0] nxt_frac_shifted; + reg [25:0] frac_shifted; + //reg nxt_sign_not_shifted; + reg sign_not_shifted; + //reg [25:0] nxt_frac_not_shifted; + reg [25:0] frac_not_shifted; + //reg [7:0] nxt_exp_max; + reg [ 7:0] exp_max; + + // MUL step1 outputs -> step2 inputs + reg mul_sign1; + reg mul_sign2; + reg [ 7:0] mul_exp1; + reg [ 7:0] mul_exp2; + reg [25:0] product; + reg mul_carry_out; + + reg [61:0] step1_to_step2; + reg [61:0] nxt_step1_to_step2; + + + // ADD step2 outputs -> step3 inputs + reg add_sign_out; + //reg nxt_sign_out; + reg [25:0] add_sum; //reg [25:0] nxt_sum; - reg add_carry_out; - //reg nxt_carry_out; - reg [7:0] add_exp_max; - //reg [7:0] nxt_exp_max_out; - - // MUL step2 outputs -> step3 inputs - reg mul_sign_out; - reg [7:0] sum_exp; - reg mul_ovf; - reg mul_unf; - - // invalid operation flag - reg inv; - reg inv2; - reg inv3; - - - reg [37:0] step2_to_step3; - reg [37:0] nxt_step2_to_step3; - - // right shift smaller fraction by difference in exponents - - ADD_step1 addStep1( - .floating_point1_in(floating_point1), - .floating_point2_in(floating_point2), - .sign_shifted(sign_shifted), - .frac_shifted(frac_shifted), - .sign_not_shifted(sign_not_shifted), - .frac_not_shifted(frac_not_shifted), - .exp_max(exp_max) - ); - - // multiply fractions - - MUL_step1 mulStep1( - .fp1_in(floating_point1), - .fp2_in(floating_point2), - .sign1(mul_sign1), - .sign2(mul_sign2), - .exp1(mul_exp1), - .exp2(mul_exp2), - .product(product), - .carry_out(mul_carry_out) - ); - - always_comb begin : check_for_invalid_op - inv = 0; - if(funct7 == ADD) begin - if((floating_point1[30:0] == 31'h7F800000) && - (floating_point2[30:0] == 31'h7F800000) && + reg add_carry_out; + //reg nxt_carry_out; + reg [ 7:0] add_exp_max; + //reg [7:0] nxt_exp_max_out; + + // MUL step2 outputs -> step3 inputs + reg mul_sign_out; + reg [ 7:0] sum_exp; + reg mul_ovf; + reg mul_unf; + + // invalid operation flag + reg inv; + reg inv2; + reg inv3; + + + reg [37:0] step2_to_step3; + reg [37:0] nxt_step2_to_step3; + + // right shift smaller fraction by difference in exponents + + ADD_step1 addStep1 ( + .floating_point1_in(floating_point1), + .floating_point2_in(floating_point2), + .sign_shifted(sign_shifted), + .frac_shifted(frac_shifted), + .sign_not_shifted(sign_not_shifted), + .frac_not_shifted(frac_not_shifted), + .exp_max(exp_max) + ); + + // multiply fractions + + MUL_step1 mulStep1 ( + .fp1_in(floating_point1), + .fp2_in(floating_point2), + .sign1(mul_sign1), + .sign2(mul_sign2), + .exp1(mul_exp1), + .exp2(mul_exp2), + .product(product), + .carry_out(mul_carry_out) + ); + + always_comb begin : check_for_invalid_op + inv = 0; + if (funct7 == ADD) begin + if((floating_point1[30:0] == 31'h7F800000) && + (floating_point2[30:0] == 31'h7F800000) && (floating_point1[31] ^ floating_point2[31])) begin - inv = 1; - end - end - - if(funct7 == MUL) begin - if(((floating_point1[30:0] == 31'h00000000) && + inv = 1; + end + end + + if (funct7 == MUL) begin + if(((floating_point1[30:0] == 31'h00000000) && (floating_point2[30:0] == 31'h7F800000)) || ((floating_point1[30:0] == 31'h7F800000) && (floating_point2[30:0] == 31'h00000000))) begin - inv = 1; - end - end - end // block: check_for_invalid_op - - - always_comb begin : select_op_step1to2 - case(funct7) - ADD: begin - nxt_step1_to_step2[61] = sign_shifted; - nxt_step1_to_step2[60:35] = frac_shifted; - nxt_step1_to_step2[34] = sign_not_shifted; - nxt_step1_to_step2[33:8] = frac_not_shifted; - nxt_step1_to_step2[7:0] = exp_max; - end - MUL: begin - nxt_step1_to_step2[61] = mul_sign1; - nxt_step1_to_step2[60] = mul_sign2; - nxt_step1_to_step2[59:52] = mul_exp1; - nxt_step1_to_step2[51:44] = mul_exp2; - nxt_step1_to_step2[43:18] = product; - nxt_step1_to_step2[17] = mul_carry_out; - - end - - endcase // case (funct7) - end // block: select_op - - always_ff @ (posedge clk, negedge nrst) begin : STEP1_to_STEP2 - if(nrst == 0) begin - frm2 <= 0; - step1_to_step2 <= 0; - funct7_2 <= 0; - inv2 <= 0;/* + inv = 1; + end + end + end // block: check_for_invalid_op + + + always_comb begin : select_op_step1to2 + case (funct7) + ADD: begin + nxt_step1_to_step2[61] = sign_shifted; + nxt_step1_to_step2[60:35] = frac_shifted; + nxt_step1_to_step2[34] = sign_not_shifted; + nxt_step1_to_step2[33:8] = frac_not_shifted; + nxt_step1_to_step2[7:0] = exp_max; + end + MUL: begin + nxt_step1_to_step2[61] = mul_sign1; + nxt_step1_to_step2[60] = mul_sign2; + nxt_step1_to_step2[59:52] = mul_exp1; + nxt_step1_to_step2[51:44] = mul_exp2; + nxt_step1_to_step2[43:18] = product; + nxt_step1_to_step2[17] = mul_carry_out; + + end + + endcase // case (funct7) + end // block: select_op + + always_ff @(posedge clk, negedge nrst) begin : STEP1_to_STEP2 + if (nrst == 0) begin + frm2 <= 0; + step1_to_step2 <= 0; + funct7_2 <= 0; + inv2 <= 0; /* sign_shifted <= 0; frac_shifted <= 0; sign_not_shifted <= 0; frac_not_shifted <= 0; exp_max <= 0;*/ - end - else begin - frm2 <= frm; - step1_to_step2 <= nxt_step1_to_step2; - funct7_2 <= funct7; - inv2 <= inv;/* + end else begin + frm2 <= frm; + step1_to_step2 <= nxt_step1_to_step2; + funct7_2 <= funct7; + inv2 <= inv; /* sign_shifted <= nxt_sign_shifted; frac_shifted <= nxt_frac_shifted; sign_not_shifted <= nxt_sign_not_shifted; frac_not_shifted <= nxt_frac_not_shifted; exp_max <= nxt_exp_max;*/ - end - end - - // add signed fractions - - ADD_step2 add_step2 ( - .frac1(step1_to_step2[60:35]), // frac_shifted - .sign1(step1_to_step2[61]), // sign_shifted - .frac2(step1_to_step2[33:8]), // frac_not_shhifted - .sign2(step1_to_step2[34]), // sign_not_shifted - .exp_max_in(step1_to_step2[7:0]), // exp_max - .sign_out(add_sign_out), - .sum(add_sum), - .carry_out(add_carry_out), - .exp_max_out(add_exp_max) - ); - - // add exponents and xor sign bits - - MUL_step2 mul_step2 ( - .sign1(step1_to_step2[61]), // mul_sign1 - .sign2(step1_to_step2[60]), // mul_sign2 - .exp1(step1_to_step2[59:52]), // mul_exp1 - .exp2(step1_to_step2[51:44]), // mul_exp2 - .sign_out(mul_sign_out), - .sum_exp(sum_exp), - .ovf(mul_ovf), - .unf(mul_unf), - .carry(step1_to_step2[17]) - ); - - always_comb begin : select_op_step2to3 - case(funct7_2) - ADD: begin - nxt_step2_to_step3[37:36]= 2'b00; - nxt_step2_to_step3[35] = add_sign_out; - nxt_step2_to_step3[34:9] = add_sum; - nxt_step2_to_step3[8] = add_carry_out; - nxt_step2_to_step3[7:0] = add_exp_max; - end - MUL: begin - nxt_step2_to_step3[37] = mul_ovf; - nxt_step2_to_step3[36] = mul_unf; - nxt_step2_to_step3[35] = mul_sign_out; - nxt_step2_to_step3[34:9] = step1_to_step2[43:18]; // product from MUL_step1 - nxt_step2_to_step3[8] = step1_to_step2[17]; // mul_carry_out; - nxt_step2_to_step3[7:0] = sum_exp; - - end - endcase // case (funct7_2) - end // block: select_op_step2to3 - - - always_ff @ (posedge clk, negedge nrst) begin : STEP2_to_STEP3 - if(nrst == 0) begin - funct7_3 <= 0; - step2_to_step3 <= 0; - frm3 <= 0; - inv3 <= 0; - end - else begin - funct7_3 <= funct7_2; - step2_to_step3 <= nxt_step2_to_step3; - frm3 <= frm2; - inv3 <= inv2; - end - end - - //shift fraction until 1 is the first digit - //round based on rounding mode - reg o; - - always_comb begin - if((step2_to_step3[7:0] == 8'b11111111) && (step2_to_step3[36] == 1'b0) && (step2_to_step3[8] == 0)) o = 1; - else o = step2_to_step3[37]; - end - - ADD_step3 step3 ( - .ovf_in(o), - .unf_in(step2_to_step3[36]), - .dz(1'b0), - .inv(inv3), - .frm(frm3), - .exponent_max_in(step2_to_step3[7:0]), - .sign_in(step2_to_step3[35]), - .frac_in(step2_to_step3[34:9]), - .carry_out(step2_to_step3[8]), - .floating_point_out(floating_point_out), - .flags(flags) - ); - - endmodule + end + end + + // add signed fractions + + ADD_step2 add_step2 ( + .frac1 (step1_to_step2[60:35]), // frac_shifted + .sign1 (step1_to_step2[61]), // sign_shifted + .frac2 (step1_to_step2[33:8]), // frac_not_shhifted + .sign2 (step1_to_step2[34]), // sign_not_shifted + .exp_max_in (step1_to_step2[7:0]), // exp_max + .sign_out (add_sign_out), + .sum (add_sum), + .carry_out (add_carry_out), + .exp_max_out(add_exp_max) + ); + + // add exponents and xor sign bits + + MUL_step2 mul_step2 ( + .sign1(step1_to_step2[61]), // mul_sign1 + .sign2(step1_to_step2[60]), // mul_sign2 + .exp1(step1_to_step2[59:52]), // mul_exp1 + .exp2(step1_to_step2[51:44]), // mul_exp2 + .sign_out(mul_sign_out), + .sum_exp(sum_exp), + .ovf(mul_ovf), + .unf(mul_unf), + .carry(step1_to_step2[17]) + ); + + always_comb begin : select_op_step2to3 + case (funct7_2) + ADD: begin + nxt_step2_to_step3[37:36]= 2'b00; + nxt_step2_to_step3[35] = add_sign_out; + nxt_step2_to_step3[34:9] = add_sum; + nxt_step2_to_step3[8] = add_carry_out; + nxt_step2_to_step3[7:0] = add_exp_max; + end + MUL: begin + nxt_step2_to_step3[37] = mul_ovf; + nxt_step2_to_step3[36] = mul_unf; + nxt_step2_to_step3[35] = mul_sign_out; + nxt_step2_to_step3[34:9] = step1_to_step2[43:18]; // product from MUL_step1 + nxt_step2_to_step3[8] = step1_to_step2[17]; // mul_carry_out; + nxt_step2_to_step3[7:0] = sum_exp; + + end + endcase // case (funct7_2) + end // block: select_op_step2to3 + + + always_ff @(posedge clk, negedge nrst) begin : STEP2_to_STEP3 + if (nrst == 0) begin + funct7_3 <= 0; + step2_to_step3 <= 0; + frm3 <= 0; + inv3 <= 0; + end else begin + funct7_3 <= funct7_2; + step2_to_step3 <= nxt_step2_to_step3; + frm3 <= frm2; + inv3 <= inv2; + end + end + + //shift fraction until 1 is the first digit + //round based on rounding mode + reg o; + + always_comb begin + if((step2_to_step3[7:0] == 8'b11111111) && (step2_to_step3[36] == 1'b0) && (step2_to_step3[8] == 0)) + o = 1; + else o = step2_to_step3[37]; + end + + ADD_step3 step3 ( + .ovf_in(o), + .unf_in(step2_to_step3[36]), + .dz(1'b0), + .inv(inv3), + .frm(frm3), + .exponent_max_in(step2_to_step3[7:0]), + .sign_in(step2_to_step3[35]), + .frac_in(step2_to_step3[34:9]), + .carry_out(step2_to_step3[8]), + .floating_point_out(floating_point_out), + .flags(flags) + ); + +endmodule diff --git a/source_code/Floating_Point_Unit/source/MUL_step1.sv b/source_code/Floating_Point_Unit/source/MUL_step1.sv index 10c8dd47a..1d522a119 100644 --- a/source_code/Floating_Point_Unit/source/MUL_step1.sv +++ b/source_code/Floating_Point_Unit/source/MUL_step1.sv @@ -13,31 +13,30 @@ // product - result of fraction multiplication // carry_out - signal if there is a carry out of the multiplication -module MUL_step1 - ( - input [31:0] fp1_in, - input [31:0] fp2_in, - output sign1, - output sign2, - output [7:0] exp1, - output [7:0] exp2, - output [25:0] product, - output carry_out - ); +module MUL_step1 ( + input [31:0] fp1_in, + input [31:0] fp2_in, + output sign1, + output sign2, + output [ 7:0] exp1, + output [ 7:0] exp2, + output [25:0] product, + output carry_out +); - assign sign1 = fp1_in[31]; - assign sign2 = fp2_in[31]; - assign exp1 = fp1_in[30:23]; - assign exp2 = fp2_in[30:23]; + assign sign1 = fp1_in[31]; + assign sign2 = fp2_in[31]; + assign exp1 = fp1_in[30:23]; + assign exp2 = fp2_in[30:23]; - mul_26b MUL ( - .frac_in1({1'b1, fp1_in[22:0], 2'b00}), - .frac_in2({1'b1, fp2_in[22:0], 2'b00}), - .frac_out(product), - .overflow(carry_out) - ); + mul_26b MUL ( + .frac_in1({1'b1, fp1_in[22:0], 2'b00}), + .frac_in2({1'b1, fp2_in[22:0], 2'b00}), + .frac_out(product), + .overflow(carry_out) + ); + +endmodule // MUL_step1 -endmodule // MUL_step1 - diff --git a/source_code/Floating_Point_Unit/source/MUL_step2.sv b/source_code/Floating_Point_Unit/source/MUL_step2.sv index bf7167183..4d6eab8cf 100644 --- a/source_code/Floating_Point_Unit/source/MUL_step2.sv +++ b/source_code/Floating_Point_Unit/source/MUL_step2.sv @@ -11,32 +11,31 @@ //Outputs: // sign_out - result of xor operation // sum_exp - result of addition -// ovf - signal if an overflow has occurred +// ovf - signal if an overflow has occurred // unf - signal if an undeflow has occurred -module MUL_step2 - ( - input sign1, - input sign2, - input [7:0] exp1, - input [7:0] exp2, - output sign_out, - output [7:0] sum_exp, - output reg ovf, - output reg unf, - input carry - ); - +module MUL_step2 ( + input sign1, + input sign2, + input [7:0] exp1, + input [7:0] exp2, + output sign_out, + output [7:0] sum_exp, + output reg ovf, + output reg unf, + input carry +); - adder_8b add_EXPs( - .carry(carry), - .exp1(exp1), - .exp2(exp2), - .sum(sum_exp), - .ovf(ovf), - .unf(unf) - ); - - assign sign_out = sign1 ^ sign2; + + adder_8b add_EXPs ( + .carry(carry), + .exp1 (exp1), + .exp2 (exp2), + .sum (sum_exp), + .ovf (ovf), + .unf (unf) + ); + + assign sign_out = sign1 ^ sign2; endmodule diff --git a/source_code/Floating_Point_Unit/source/adder_26b.sv b/source_code/Floating_Point_Unit/source/adder_26b.sv index 57b00709c..22232f579 100644 --- a/source_code/Floating_Point_Unit/source/adder_26b.sv +++ b/source_code/Floating_Point_Unit/source/adder_26b.sv @@ -1,36 +1,36 @@ //By : Joe Nasti //Last Updated : 7.16.18 // -//Module Summary: +//Module Summary: // adds two signed 26 bit fraction values // //Inputs: // frac1/2 - signed 26 bit values with decimal point fixed after second bit //Outputs: // sum - output of sum operation regardless of overflow -// ovf - high if an overflow has occured - -module adder_26b( - input [26:0] frac1, - input [26:0] frac2, - output reg [26:0] sum, - output reg ovf +// ovf - high if an overflow has occured + +module adder_26b ( + input [26:0] frac1, + input [26:0] frac2, + output reg [26:0] sum, + output reg ovf ); -always_comb begin + always_comb begin + + sum = frac1 + frac2; + ovf = 0; + + if (frac1[26] == 1 && frac2[26] == 1 && sum[26] == 0) begin + ovf = 1; + sum[26] = 1; + end + + if (frac1[26] == 0 && frac2[26] == 0 && sum[26] == 1) begin + ovf = 1; + sum[26] = 0; + end - sum = frac1 + frac2; - ovf = 0; - - if(frac1[26] == 1 && frac2[26]== 1 && sum[26] == 0) begin - ovf = 1; - sum[26] = 1; - end - - if(frac1[26] == 0 && frac2[26]== 0 && sum[26] == 1) begin - ovf = 1; - sum[26] = 0; - end - -end + end endmodule diff --git a/source_code/Floating_Point_Unit/source/adder_8b.sv b/source_code/Floating_Point_Unit/source/adder_8b.sv index acfe38116..f6b0f9d4a 100644 --- a/source_code/Floating_Point_Unit/source/adder_8b.sv +++ b/source_code/Floating_Point_Unit/source/adder_8b.sv @@ -10,28 +10,28 @@ // ovf - signal overflow has occurred // unf - signal underflow has occurred -module adder_8b( - input carry, - input [7:0] exp1, - input [7:0] exp2, - output [7:0] sum, - output ovf, - output unf +module adder_8b ( + input carry, + input [7:0] exp1, + input [7:0] exp2, + output [7:0] sum, + output ovf, + output unf ); - reg [7:0] r_exp1; - reg [7:0] r_exp2; - reg [7:0] r_sum; - + reg [7:0] r_exp1; + reg [7:0] r_exp2; + reg [7:0] r_sum; - always_comb begin - r_exp1 = exp1 - 8'b10000000; - r_exp2 = exp2 - 8'b10000000; - r_sum = r_exp1 + r_exp2; - end - - assign sum = (exp1 + exp2) - 8'b10000000; // add with offset - assign ovf = r_sum[7] && ~r_exp1[7] && ~r_exp2[7]; - assign unf = ((carry != 1) || (sum != 8'b11111111)) && (~r_sum[7] && r_exp1[7] && r_exp2[7]); + + always_comb begin + r_exp1 = exp1 - 8'b10000000; + r_exp2 = exp2 - 8'b10000000; + r_sum = r_exp1 + r_exp2; + end + + assign sum = (exp1 + exp2) - 8'b10000000; // add with offset + assign ovf = r_sum[7] && ~r_exp1[7] && ~r_exp2[7]; + assign unf = ((carry != 1) || (sum != 8'b11111111)) && (~r_sum[7] && r_exp1[7] && r_exp2[7]); endmodule diff --git a/source_code/Floating_Point_Unit/source/int_compare.sv b/source_code/Floating_Point_Unit/source/int_compare.sv index 41e041c2d..2adbf1288 100644 --- a/source_code/Floating_Point_Unit/source/int_compare.sv +++ b/source_code/Floating_Point_Unit/source/int_compare.sv @@ -4,35 +4,35 @@ //Module Summary: // Compares magnitude of two unsigned 8 bit integers // -//Inputs: +//Inputs: // exp1/2 - 8 bit unsigned integers //Outputs: // u_diff - unsigned difference between exp1 and exp2 // cmp_out - exp1 < exp2 -> 1, exp1 >= exp2 -> 0 -module int_compare( - input [7:0] exp1, - input [7:0] exp2, - output [7:0] u_diff, - output reg cmp_out +module int_compare ( + input [7:0] exp1, + input [7:0] exp2, + output [7:0] u_diff, + output reg cmp_out ); -wire [8:0] u_exp1 = {1'b0, exp1}; -wire [8:0] u_exp2 = {1'b0, exp2}; -reg [8:0] diff; + wire [8:0] u_exp1 = {1'b0, exp1}; + wire [8:0] u_exp2 = {1'b0, exp2}; + reg [8:0] diff; -assign u_diff = diff[7:0]; + assign u_diff = diff[7:0]; -always_comb begin - diff = u_exp1 - u_exp2; - case(diff[8]) - 1'b0: cmp_out = 1'b0; - 1'b1: begin - cmp_out = 1'b1; - diff = -diff; - end - endcase -end + always_comb begin + diff = u_exp1 - u_exp2; + case (diff[8]) + 1'b0: cmp_out = 1'b0; + 1'b1: begin + cmp_out = 1'b1; + diff = -diff; + end + endcase + end endmodule diff --git a/source_code/Floating_Point_Unit/source/left_shift.sv b/source_code/Floating_Point_Unit/source/left_shift.sv index 8b6c0c2a0..e08cf47c2 100644 --- a/source_code/Floating_Point_Unit/source/left_shift.sv +++ b/source_code/Floating_Point_Unit/source/left_shift.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/16/2018 // -//Module Summary: +//Module Summary: // Left shifts an unsigned 26 bit value until the first '1' is the most significant bit and returns the amount shifted // //Inputs: @@ -9,41 +9,116 @@ //Outputs: // result - resulting 26 bit value with a '1' in most significance and zeros shifted in from the right -module left_shift( - input [25:0] fraction, - output reg [25:0] result, - output reg [7:0] shifted_amount +module left_shift ( + input [25:0] fraction, + output reg [25:0] result, + output reg [ 7:0] shifted_amount ); - always_comb begin - result = fraction; - shifted_amount = 0; - casez(fraction) - 26'b01????????????????????????: begin result = {fraction[24:0], 1'd0}; shifted_amount = 1; end - 26'b001???????????????????????: begin result = {fraction[23:0], 2'd0}; shifted_amount = 2; end - 26'b0001??????????????????????: begin result = {fraction[22:0], 3'd0}; shifted_amount = 3; end - 26'b00001?????????????????????: begin result = {fraction[21:0], 4'd0}; shifted_amount = 4; end - 26'b000001????????????????????: begin result = {fraction[20:0], 5'd0}; shifted_amount = 5; end - 26'b0000001???????????????????: begin result = {fraction[19:0], 6'd0}; shifted_amount = 6; end - 26'b00000001??????????????????: begin result = {fraction[18:0], 7'd0}; shifted_amount = 7; end - 26'b000000001?????????????????: begin result = {fraction[17:0], 8'd0}; shifted_amount = 8; end - 26'b0000000001????????????????: begin result = {fraction[16:0], 9'd0}; shifted_amount = 9; end - 26'b00000000001???????????????: begin result = {fraction[15:0],10'd0}; shifted_amount = 10; end - 26'b000000000001??????????????: begin result = {fraction[14:0],11'd0}; shifted_amount = 11; end - 26'b0000000000001?????????????: begin result = {fraction[13:0],12'd0}; shifted_amount = 12; end - 26'b00000000000001????????????: begin result = {fraction[12:0],13'd0}; shifted_amount = 13; end - 26'b000000000000001???????????: begin result = {fraction[11:0],14'd0}; shifted_amount = 14; end - 26'b0000000000000001??????????: begin result = {fraction[10:0],15'd0}; shifted_amount = 15; end - 26'b00000000000000001?????????: begin result = {fraction[9:0], 16'd0}; shifted_amount = 16; end - 26'b000000000000000001????????: begin result = {fraction[8:0], 17'd0}; shifted_amount = 17; end - 26'b0000000000000000001???????: begin result = {fraction[7:0], 18'd0}; shifted_amount = 18; end - 26'b00000000000000000001??????: begin result = {fraction[6:0], 19'd0}; shifted_amount = 19; end - 26'b000000000000000000001?????: begin result = {fraction[5:0], 20'd0}; shifted_amount = 20; end - 26'b0000000000000000000001????: begin result = {fraction[4:0], 21'd0}; shifted_amount = 21; end - 26'b00000000000000000000001???: begin result = {fraction[3:0], 22'd0}; shifted_amount = 22; end - 26'b000000000000000000000001??: begin result = {fraction[2:0], 23'd0}; shifted_amount = 23; end - 26'b0000000000000000000000001?: begin result = {fraction[1:0], 24'd0}; shifted_amount = 24; end - 26'b00000000000000000000000001: begin result = {fraction[0], 25'd0}; shifted_amount = 25; end - endcase - end + always_comb begin + result = fraction; + shifted_amount = 0; + casez (fraction) + 26'b01????????????????????????: begin + result = {fraction[24:0], 1'd0}; + shifted_amount = 1; + end + 26'b001???????????????????????: begin + result = {fraction[23:0], 2'd0}; + shifted_amount = 2; + end + 26'b0001??????????????????????: begin + result = {fraction[22:0], 3'd0}; + shifted_amount = 3; + end + 26'b00001?????????????????????: begin + result = {fraction[21:0], 4'd0}; + shifted_amount = 4; + end + 26'b000001????????????????????: begin + result = {fraction[20:0], 5'd0}; + shifted_amount = 5; + end + 26'b0000001???????????????????: begin + result = {fraction[19:0], 6'd0}; + shifted_amount = 6; + end + 26'b00000001??????????????????: begin + result = {fraction[18:0], 7'd0}; + shifted_amount = 7; + end + 26'b000000001?????????????????: begin + result = {fraction[17:0], 8'd0}; + shifted_amount = 8; + end + 26'b0000000001????????????????: begin + result = {fraction[16:0], 9'd0}; + shifted_amount = 9; + end + 26'b00000000001???????????????: begin + result = {fraction[15:0], 10'd0}; + shifted_amount = 10; + end + 26'b000000000001??????????????: begin + result = {fraction[14:0], 11'd0}; + shifted_amount = 11; + end + 26'b0000000000001?????????????: begin + result = {fraction[13:0], 12'd0}; + shifted_amount = 12; + end + 26'b00000000000001????????????: begin + result = {fraction[12:0], 13'd0}; + shifted_amount = 13; + end + 26'b000000000000001???????????: begin + result = {fraction[11:0], 14'd0}; + shifted_amount = 14; + end + 26'b0000000000000001??????????: begin + result = {fraction[10:0], 15'd0}; + shifted_amount = 15; + end + 26'b00000000000000001?????????: begin + result = {fraction[9:0], 16'd0}; + shifted_amount = 16; + end + 26'b000000000000000001????????: begin + result = {fraction[8:0], 17'd0}; + shifted_amount = 17; + end + 26'b0000000000000000001???????: begin + result = {fraction[7:0], 18'd0}; + shifted_amount = 18; + end + 26'b00000000000000000001??????: begin + result = {fraction[6:0], 19'd0}; + shifted_amount = 19; + end + 26'b000000000000000000001?????: begin + result = {fraction[5:0], 20'd0}; + shifted_amount = 20; + end + 26'b0000000000000000000001????: begin + result = {fraction[4:0], 21'd0}; + shifted_amount = 21; + end + 26'b00000000000000000000001???: begin + result = {fraction[3:0], 22'd0}; + shifted_amount = 22; + end + 26'b000000000000000000000001??: begin + result = {fraction[2:0], 23'd0}; + shifted_amount = 23; + end + 26'b0000000000000000000000001?: begin + result = {fraction[1:0], 24'd0}; + shifted_amount = 24; + end + 26'b00000000000000000000000001: begin + result = {fraction[0], 25'd0}; + shifted_amount = 25; + end + endcase + end endmodule diff --git a/source_code/Floating_Point_Unit/source/max_select.sv b/source_code/Floating_Point_Unit/source/max_select.sv index 804bf528c..e581d74e6 100644 --- a/source_code/Floating_Point_Unit/source/max_select.sv +++ b/source_code/Floating_Point_Unit/source/max_select.sv @@ -1,23 +1,21 @@ // not used -module max_select( - input [7:0] exp1, - input [7:0] exp2, - output reg [7:0] max +module max_select ( + input [7:0] exp1, + input [7:0] exp2, + output reg [7:0] max ); -reg [8:0] u_exp1; -reg [8:0] u_exp2; -reg [8:0] diff; + reg [8:0] u_exp1; + reg [8:0] u_exp2; + reg [8:0] diff; - assign u_exp1 = {1'b0, exp1}; - assign u_exp2 = {1'b0, exp2}; - assign diff = u_exp1 - u_exp2; + assign u_exp1 = {1'b0, exp1}; + assign u_exp2 = {1'b0, exp2}; + assign diff = u_exp1 - u_exp2; -always_comb begin - if(diff[8] == 0) - max = exp1; - else - max = exp2; -end + always_comb begin + if (diff[8] == 0) max = exp1; + else max = exp2; + end endmodule diff --git a/source_code/Floating_Point_Unit/source/mul_26b.sv b/source_code/Floating_Point_Unit/source/mul_26b.sv index fb3cb91ad..3af7128c3 100644 --- a/source_code/Floating_Point_Unit/source/mul_26b.sv +++ b/source_code/Floating_Point_Unit/source/mul_26b.sv @@ -6,25 +6,24 @@ // //Inputs: // frac_in1/2 - 26 bit fractions with decimal point after first bit -//Outputs: +//Outputs: // frac_out - 26 bit result of operation regardless of overflow occuring // overflow - flags if an overflow has occured -module mul_26b -( - input [25:0] frac_in1, - input [25:0] frac_in2, - output [25:0] frac_out, - output overflow - ); +module mul_26b ( + input [25:0] frac_in1, + input [25:0] frac_in2, + output [25:0] frac_out, + output overflow +); - reg [51:0] frac_out_52b; + reg [51:0] frac_out_52b; - assign overflow = frac_out_52b[51]; - assign frac_out = frac_out_52b[50:25]; + assign overflow = frac_out_52b[51]; + assign frac_out = frac_out_52b[50:25]; - always_comb begin : MULTIPLY - frac_out_52b = frac_in1 * frac_in2; - - end -endmodule + always_comb begin : MULTIPLY + frac_out_52b = frac_in1 * frac_in2; + + end +endmodule diff --git a/source_code/Floating_Point_Unit/source/right_shift.sv b/source_code/Floating_Point_Unit/source/right_shift.sv index 0ed6f3b3c..cf325d47f 100644 --- a/source_code/Floating_Point_Unit/source/right_shift.sv +++ b/source_code/Floating_Point_Unit/source/right_shift.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/18/18 // -//Module Summary: +//Module Summary: // right shifts a 26 bit value by a given amount // //Inputs: @@ -10,42 +10,42 @@ //Outputs: // result - 26 bit result of the shift -module right_shift( - input [25:0] fraction, - input [7:0] shift_amount, - output reg [25:0] result +module right_shift ( + input [25:0] fraction, + input [ 7:0] shift_amount, + output reg [25:0] result ); - always_comb begin - case(shift_amount) - 0 : result = fraction; - 1 : result = {1'd0, fraction[25:1]}; - 2 : result = {2'd0, fraction[25:2]}; - 3 : result = {3'd0, fraction[25:3]}; - 4 : result = {4'd0, fraction[25:4]}; - 5 : result = {5'd0, fraction[25:5]}; - 6 : result = {6'd0, fraction[25:6]}; - 7 : result = {7'd0, fraction[25:7]}; - 8 : result = {8'd0, fraction[25:8]}; - 9 : result = {9'd0, fraction[25:9]}; - 10: result = {10'd0, fraction[25:10]}; - 11: result = {11'd0, fraction[25:11]}; - 12: result = {12'd0, fraction[25:12]}; - 13: result = {13'd0, fraction[25:13]}; - 14: result = {14'd0, fraction[25:14]}; - 15: result = {15'd0, fraction[25:15]}; - 16: result = {16'd0, fraction[25:16]}; - 17: result = {17'd0, fraction[25:17]}; - 18: result = {18'd0, fraction[25:18]}; - 19: result = {19'd0, fraction[25:19]}; - 20: result = {20'd0, fraction[25:20]}; - 21: result = {21'd0, fraction[25:21]}; - 22: result = {22'd0, fraction[25:22]}; - 23: result = {23'd0, fraction[25:23]}; - 24: result = {24'd0, fraction[25:24]}; - 25: result = {25'd0, fraction[25]}; - default: result = 26'd0; - endcase - end + always_comb begin + case (shift_amount) + 0: result = fraction; + 1: result = {1'd0, fraction[25:1]}; + 2: result = {2'd0, fraction[25:2]}; + 3: result = {3'd0, fraction[25:3]}; + 4: result = {4'd0, fraction[25:4]}; + 5: result = {5'd0, fraction[25:5]}; + 6: result = {6'd0, fraction[25:6]}; + 7: result = {7'd0, fraction[25:7]}; + 8: result = {8'd0, fraction[25:8]}; + 9: result = {9'd0, fraction[25:9]}; + 10: result = {10'd0, fraction[25:10]}; + 11: result = {11'd0, fraction[25:11]}; + 12: result = {12'd0, fraction[25:12]}; + 13: result = {13'd0, fraction[25:13]}; + 14: result = {14'd0, fraction[25:14]}; + 15: result = {15'd0, fraction[25:15]}; + 16: result = {16'd0, fraction[25:16]}; + 17: result = {17'd0, fraction[25:17]}; + 18: result = {18'd0, fraction[25:18]}; + 19: result = {19'd0, fraction[25:19]}; + 20: result = {20'd0, fraction[25:20]}; + 21: result = {21'd0, fraction[25:21]}; + 22: result = {22'd0, fraction[25:22]}; + 23: result = {23'd0, fraction[25:23]}; + 24: result = {24'd0, fraction[25:24]}; + 25: result = {25'd0, fraction[25]}; + default: result = 26'd0; + endcase + end endmodule diff --git a/source_code/Floating_Point_Unit/source/rounder.sv b/source_code/Floating_Point_Unit/source/rounder.sv index f8fc6095c..82c1caf62 100644 --- a/source_code/Floating_Point_Unit/source/rounder.sv +++ b/source_code/Floating_Point_Unit/source/rounder.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/18/18 // -//Module Summary: +//Module Summary: // rounds floating point after the operation according to the frm (rounding mode) // //Inputs: @@ -12,50 +12,42 @@ //Ouputs: // rount_out - resulting floating point after rounding operation -module rounder( - input [2:0] frm, - input sign, - input [7:0] exp_in, - input [24:0] fraction, - output reg [31:0] round_out, - output rounded - ); - reg round_amount; +module rounder ( + input [ 2:0] frm, + input sign, + input [ 7:0] exp_in, + input [24:0] fraction, + output reg [31:0] round_out, + output rounded +); + reg round_amount; - localparam RNE = 3'b000; - localparam RZE = 3'b001; - localparam RDN = 3'b010; - localparam RUP = 3'b011; - localparam RMM = 3'b100; - - always_comb begin - round_amount = 0; - if(fraction[24:2] != '1) begin - if(frm == RNE) begin - if(fraction[1:0] == 2'b11) - round_amount = 1; - end - else if(frm == RZE) begin - round_amount = 0; - end - else if(frm == RDN) begin - if(sign == 1 && ((fraction[0] == 1) || (fraction[1] == 1))) - round_amount = 1; - end - else if(frm == RUP) begin - if(sign == 0 && ((fraction[0] == 1) || (fraction[1] == 1))) - round_amount = 1; - end - else if(frm == RMM) begin - if(fraction[1] == 1) - round_amount = 1; - end - end // if (fraction[24:2] != '1) - end // always_comb + localparam RNE = 3'b000; + localparam RZE = 3'b001; + localparam RDN = 3'b010; + localparam RUP = 3'b011; + localparam RMM = 3'b100; + + always_comb begin + round_amount = 0; + if (fraction[24:2] != '1) begin + if (frm == RNE) begin + if (fraction[1:0] == 2'b11) round_amount = 1; + end else if (frm == RZE) begin + round_amount = 0; + end else if (frm == RDN) begin + if (sign == 1 && ((fraction[0] == 1) || (fraction[1] == 1))) round_amount = 1; + end else if (frm == RUP) begin + if (sign == 0 && ((fraction[0] == 1) || (fraction[1] == 1))) round_amount = 1; + end else if (frm == RMM) begin + if (fraction[1] == 1) round_amount = 1; + end + end // if (fraction[24:2] != '1) + end // always_comb + + assign rounded = round_amount; + assign round_out = {sign, exp_in, fraction[24:2] + round_amount}; - assign rounded = round_amount; - assign round_out = {sign, exp_in, fraction[24:2] + round_amount}; - endmodule - - + + diff --git a/source_code/Floating_Point_Unit/source/s_to_u.sv b/source_code/Floating_Point_Unit/source/s_to_u.sv index 8725b9536..cbd64c1df 100644 --- a/source_code/Floating_Point_Unit/source/s_to_u.sv +++ b/source_code/Floating_Point_Unit/source/s_to_u.sv @@ -6,26 +6,26 @@ // //Inputs: // frac_signed - 27 bit signed value -//Outputs: +//Outputs: // sign - 1 bit sign 'frac_unsigned' // frac_unsigned - 26 bit magnitude of 'frac_signed' -module s_to_u( - input [26:0] frac_signed, - output reg sign, - output [25:0] frac_unsigned +module s_to_u ( + input [26:0] frac_signed, + output reg sign, + output [25:0] frac_unsigned ); -reg [26:0] rfrac_signed; + reg [26:0] rfrac_signed; -assign frac_unsigned = rfrac_signed[25:0]; - -always_comb begin + assign frac_unsigned = rfrac_signed[25:0]; + + always_comb begin sign = 0; - rfrac_signed = frac_signed; - if(frac_signed[26] == 1) begin - rfrac_signed = -frac_signed; - sign = 1; - end -end + rfrac_signed = frac_signed; + if (frac_signed[26] == 1) begin + rfrac_signed = -frac_signed; + sign = 1; + end + end endmodule diff --git a/source_code/Floating_Point_Unit/source/subtract.sv b/source_code/Floating_Point_Unit/source/subtract.sv index 27a783ffa..00da21ce7 100644 --- a/source_code/Floating_Point_Unit/source/subtract.sv +++ b/source_code/Floating_Point_Unit/source/subtract.sv @@ -1,21 +1,20 @@ // subtracts shifted amount from the exponent -module subtract -( - input [7:0] exp1, - input [7:0] shifted_amount, - output [7:0] result +module subtract ( + input [7:0] exp1, + input [7:0] shifted_amount, + output [7:0] result ); - reg [8:0] u_exp1 = {1'b0, exp1}; - reg [8:0] u_shifted_amount = {1'b0,shifted_amount}; - reg [8:0] u_result; - -always_comb begin - u_exp1 = {1'b0, exp1}; - u_shifted_amount = {1'b0,shifted_amount}; - u_result = u_exp1 - u_shifted_amount; -end + reg [8:0] u_exp1 = {1'b0, exp1}; + reg [8:0] u_shifted_amount = {1'b0, shifted_amount}; + reg [8:0] u_result; - assign result = u_result[7:0]; -endmodule // subtract + always_comb begin + u_exp1 = {1'b0, exp1}; + u_shifted_amount = {1'b0, shifted_amount}; + u_result = u_exp1 - u_shifted_amount; + end + + assign result = u_result[7:0]; +endmodule // subtract diff --git a/source_code/Floating_Point_Unit/source/u_to_s.sv b/source_code/Floating_Point_Unit/source/u_to_s.sv index b3a851920..e0a1c6abc 100644 --- a/source_code/Floating_Point_Unit/source/u_to_s.sv +++ b/source_code/Floating_Point_Unit/source/u_to_s.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/21/18 // -//Module Summary: +//Module Summary: // Converts a one bit sign and 26 bit magnitude to a 27 bit signed value // //Inputs: @@ -10,16 +10,16 @@ //Outputs: // frac_signed - 27 bit signed result of conversion -module u_to_s( - input sign, - input [25:0] frac_unsigned, - output reg [26:0] frac_signed +module u_to_s ( + input sign, + input [25:0] frac_unsigned, + output reg [26:0] frac_signed ); -always_comb begin - frac_signed = {1'b0, frac_unsigned}; - if(sign == 1) begin - frac_signed = -frac_signed; - end -end + always_comb begin + frac_signed = {1'b0, frac_unsigned}; + if (sign == 1) begin + frac_signed = -frac_signed; + end + end endmodule diff --git a/source_code/RV32E/rv32e_reg_file.sv b/source_code/RV32E/rv32e_reg_file.sv index e70799aef..9302f11fc 100644 --- a/source_code/RV32E/rv32e_reg_file.sv +++ b/source_code/RV32E/rv32e_reg_file.sv @@ -1,12 +1,12 @@ /* * Copyright 2021 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,48 +19,47 @@ * Created by: Jiahao Xu * Email: xu1392@purdue.edu * Date Created: 2/26/2021 -* Description: Implenmentation of RV32E register file. +* Description: Implenmentation of RV32E register file. */ `include "rv32i_reg_file_if.vh" module rv32e_reg_file ( - input CLK, nRST, + input CLK, + nRST, rv32i_reg_file_if rf_if ); import rv32i_types_pkg::*; - localparam NUM_REGS = 16; + localparam int NUM_REGS = 16; - // input: - // rf_if.wen: write enable. - // rf_if.w_data [31:0]: the word to write. - // rf_if.rd [4:0]: the register that need to write. - // rf_if.rs1 [4:0]: the address of read reg 1. - // rf_if.rs2 [4:0]: the address of read reg 2. - // output - // rf_if.rs1_data [31:0]: the read data of rs1 - // rf_if.rs2_data [31:0]: the read data of rs2 + // input: + // rf_if.wen: write enable. + // rf_if.w_data [31:0]: the word to write. + // rf_if.rd [4:0]: the register that need to write. + // rf_if.rs1 [4:0]: the address of read reg 1. + // rf_if.rs2 [4:0]: the address of read reg 2. + // output + // rf_if.rs1_data [31:0]: the read data of rs1 + // rf_if.rs2_data [31:0]: the read data of rs2 - word_t [NUM_REGS-1 : 0] registers; + word_t [NUM_REGS-1 : 0] registers; always_ff @(posedge CLK, negedge nRST) begin : RV32E_REG_FILE_FF if (~nRST) begin - registers <= '0; - end - // check if the rd is valid. + registers <= '0; + end // check if the rd is valid. else if (rf_if.wen && (|rf_if.rd) && ~rf_if.rd[4]) begin // if (WEN && rd!=0 && rd<16) write. - registers[rf_if.rd] <= rf_if.w_data; - end - else begin - // else keep the current register file unchanged. - registers <= registers; + registers[rf_if.rd] <= rf_if.w_data; + end else begin + // else keep the current register file unchanged. + registers <= registers; end end - // dispose the MSB of the select line since this register file - // has only 16 registers. + // dispose the MSB of the select line since this register file + // has only 16 registers. // the select line should be in [0,15] - // if invalid rs, return 0. - assign rf_if.rs1_data = rf_if.rs1[4] ? 0 : registers[rf_if.rs1]; - assign rf_if.rs2_data = rf_if.rs2[4] ? 0 : registers[rf_if.rs2]; + // if invalid rs, return 0. + assign rf_if.rs1_data = rf_if.rs1[4] ? 0 : registers[rf_if.rs1]; + assign rf_if.rs2_data = rf_if.rs2[4] ? 0 : registers[rf_if.rs2]; endmodule diff --git a/source_code/RV32E/rv32e_wrapper.sv b/source_code/RV32E/rv32e_wrapper.sv index 818963424..7247b839f 100644 --- a/source_code/RV32E/rv32e_wrapper.sv +++ b/source_code/RV32E/rv32e_wrapper.sv @@ -1,14 +1,16 @@ `include "component_selection_defines.vh" `include "rv32i_reg_file_if.vh" module rv32e_wrapper ( - input logic CLK, nRST, + input logic CLK, + nRST, rv32i_reg_file_if rf_if ); generate case (BASE_ISA) "RV32I": rv32i_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); "RV32E": rv32e_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); - default: rv32i_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); + default: + rv32i_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); endcase endgenerate -endmodule \ No newline at end of file +endmodule diff --git a/source_code/branch_predictors/branch_predictor_wrapper.sv b/source_code/branch_predictors/branch_predictor_wrapper.sv index e65750d70..e3f0cac61 100644 --- a/source_code/branch_predictors/branch_predictor_wrapper.sv +++ b/source_code/branch_predictors/branch_predictor_wrapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,14 +27,15 @@ `include "component_selection_defines.vh" module branch_predictor_wrapper ( - input logic CLK, nRST, - predictor_pipeline_if.predictor predict_if + input logic CLK, + nRST, + predictor_pipeline_if.predictor predict_if ); - // Predictor used based on the BR_PREDICTOR_TYPE definition - generate - case (BR_PREDICTOR_TYPE) - "not_taken" : nottaken_predictor predictor(.*); - endcase - endgenerate + // Predictor used based on the BR_PREDICTOR_TYPE definition + generate + case (BR_PREDICTOR_TYPE) + "not_taken": nottaken_predictor predictor (.*); + endcase + endgenerate endmodule diff --git a/source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv b/source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv index 0bc5b4932..753628b90 100644 --- a/source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv +++ b/source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,12 +24,13 @@ `include "predictor_pipeline_if.vh" -module nottaken_predictor( - input logic CLK, nRST, - predictor_pipeline_if.predictor predict_if +module nottaken_predictor ( + input logic CLK, + nRST, + predictor_pipeline_if.predictor predict_if ); - assign predict_if.predict_taken = 0; - assign predict_if.target_addr = predict_if.current_pc + 4; + assign predict_if.predict_taken = 0; + assign predict_if.target_addr = predict_if.current_pc + 4; endmodule diff --git a/source_code/branch_predictors/wscript b/source_code/branch_predictors/wscript deleted file mode 100644 index 82c98b932..000000000 --- a/source_code/branch_predictors/wscript +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(nottaken_predictor) - -def sim_source(cnf): - cnf.recurse(nottaken_predictor) diff --git a/source_code/bus_bridges/ahb.sv b/source_code/bus_bridges/ahb.sv index 6de00fcbc..8263a16f6 100644 --- a/source_code/bus_bridges/ahb.sv +++ b/source_code/bus_bridges/ahb.sv @@ -1,104 +1,92 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* -* Filename: ahb_master.sv -* +* +* +* Filename: ahb_master.sv +* * Created by: Chuan Yean Tan * Email: tan56@purdue.edu * Date Created: 08/31/2016 -* Description: Processes read & write request into AHB-Lite protocol -* TODO: 1. HRESP -> has to be added to the state transitions +* Description: Processes read & write request into AHB-Lite protocol +* TODO: 1. HRESP -> has to be added to the state transitions */ `include "generic_bus_if.vh" -`include "ahb_if.vh" module ahb ( - input CLK, nRST, - ahb_if.ahb_m ahb_m, - generic_bus_if.generic_bus out_gen_bus_if + input CLK, + nRST, + generic_bus_if.generic_bus out_gen_bus_if, + ahb_if.manager ahb_m ); - typedef enum logic { - IDLE, - DATA - } state_t; - - state_t state, n_state; + typedef enum logic { + IDLE, + DATA + } state_t; - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) - state <= IDLE; - else - state <= n_state; - end - - always_comb begin - if((state == DATA) & !(ahb_m.HREADY)) - n_state = state; - else - n_state = out_gen_bus_if.ren | out_gen_bus_if.wen ? DATA : IDLE; - end + state_t state, n_state; - always_comb begin - if(out_gen_bus_if.byte_en == 4'b1111) - ahb_m.HSIZE = 3'b010; // word - else if(out_gen_bus_if.byte_en == 4'b1100 || out_gen_bus_if.byte_en == 4'b0011) - ahb_m.HSIZE = 3'b001; // half word - else - ahb_m.HSIZE = 3'b000; // byte - end - - always_comb - begin - if(out_gen_bus_if.ren) begin - ahb_m.HTRANS = 2'b10; - ahb_m.HWRITE = 1'b0; - ahb_m.HADDR = out_gen_bus_if.addr; - ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HPROT = 0; - ahb_m.HMASTLOCK = 0; - end - else if(out_gen_bus_if.wen) begin - ahb_m.HTRANS = 2'b10; - ahb_m.HWRITE = 1'b1; - ahb_m.HADDR = out_gen_bus_if.addr; - ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HPROT = 0; - ahb_m.HMASTLOCK = 0; - end - else begin - ahb_m.HTRANS = 2'b0; - ahb_m.HWRITE = 1'b0; - ahb_m.HADDR = 0; - ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HPROT = 0; - ahb_m.HMASTLOCK = 0; - end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) state <= IDLE; + else state <= n_state; + end - if(state == DATA) begin - ahb_m.HWDATA = out_gen_bus_if.wdata; - end - end + always_comb begin + if ((state == DATA) & !(ahb_m.HREADY)) n_state = state; + else n_state = out_gen_bus_if.ren | out_gen_bus_if.wen ? DATA : IDLE; + end + always_comb begin + if (out_gen_bus_if.byte_en == 4'b1111) ahb_m.HSIZE = 3'b010; // word + else if (out_gen_bus_if.byte_en == 4'b1100 || out_gen_bus_if.byte_en == 4'b0011) + ahb_m.HSIZE = 3'b001; // half word + else ahb_m.HSIZE = 3'b000; // byte + end - assign out_gen_bus_if.busy = state == IDLE || ~((ahb_m.HREADY && (state == DATA))); - assign out_gen_bus_if.rdata = ahb_m.HRDATA; + always_comb begin + if (out_gen_bus_if.ren) begin + ahb_m.HTRANS = 2'b10; + ahb_m.HWRITE = 1'b0; + ahb_m.HADDR = out_gen_bus_if.addr; + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HBURST = 0; + ahb_m.HMASTLOCK = 0; + end else if (out_gen_bus_if.wen) begin + ahb_m.HTRANS = 2'b10; + ahb_m.HWRITE = 1'b1; + ahb_m.HADDR = out_gen_bus_if.addr; + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HBURST = 0; + ahb_m.HMASTLOCK = 0; + end else begin + ahb_m.HTRANS = 2'b0; + ahb_m.HWRITE = 1'b0; + ahb_m.HADDR = 0; + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HBURST = 0; + ahb_m.HMASTLOCK = 0; + end + + if (state == DATA) begin + ahb_m.HWDATA = out_gen_bus_if.wdata; + end + end + + + assign out_gen_bus_if.busy = state == IDLE || ~((ahb_m.HREADY && (state == DATA))); + assign out_gen_bus_if.rdata = ahb_m.HRDATA; endmodule diff --git a/source_code/bus_bridges/apb.sv b/source_code/bus_bridges/apb.sv new file mode 100644 index 000000000..53484fe67 --- /dev/null +++ b/source_code/bus_bridges/apb.sv @@ -0,0 +1,78 @@ + +module apb ( + input CLK, nRST, + apb_if.requester apbif, + generic_bus_if.generic_bus out_gen_bus_if +); + + typedef enum logic { + IDLE, + DATA + } state_t; + + typedef struct packed { + logic [31:0] addr; + logic [31:0] wdata; + logic wen; // ren unneeded since only used in data phase, !wen -> ren + logic [3:0] strobe; + } request_t; + + state_t state, n_state; + request_t request, request_n; + + always_ff @(posedge CLK, negedge nRST) begin + if(!nRST) begin + state <= IDLE; + request <= '0; + end else begin + state <= n_state; + request <= request_n; + end + end + + always_comb begin + request_n = request; + if(state == IDLE || (state == DATA && apbif.PREADY)) begin + request_n.addr = out_gen_bus_if.addr; + request_n.wdata = out_gen_bus_if.wdata; + request_n.wen = out_gen_bus_if.wen; + request_n.strobe = out_gen_bus_if.byte_en; + end + end + + + // TODO: How does APB work with the memory controller? + always_comb begin + if(state == DATA && !apbif.PREADY) begin + n_state = state; + end else if(out_gen_bus_if.ren || out_gen_bus_if.wen) begin + n_state = DATA; + end else begin + n_state = IDLE; + end + end + + always_comb begin + if(state == IDLE) begin + apbif.PADDR = out_gen_bus_if.addr; + apbif.PSEL = (out_gen_bus_if.ren || out_gen_bus_if.wen); + apbif.PPROT = '0; + apbif.PENABLE = 0; + apbif.PWRITE = out_gen_bus_if.wen; + apbif.PSTRB = out_gen_bus_if.byte_en; + end else begin + apbif.PADDR = request.addr; + apbif.PSEL = 1'b1; + apbif.PPROT = '0; + apbif.PENABLE = 1'b1; + apbif.PWRITE = request.wen; + apbif.PSTRB = request.strobe; + end + end + + // Response + assign out_gen_bus_if.rdata = apbif.PRDATA; + assign out_gen_bus_if.busy = (state == IDLE) || ~apbif.PREADY; + assign apbif.PWDATA = out_gen_bus_if.wdata; + +endmodule diff --git a/source_code/bus_bridges/generic_nonpipeline.sv b/source_code/bus_bridges/generic_nonpipeline.sv index b5e20fab1..106e51837 100644 --- a/source_code/bus_bridges/generic_nonpipeline.sv +++ b/source_code/bus_bridges/generic_nonpipeline.sv @@ -1,64 +1,60 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: generic_nonpipeline.sv -* +* * Created by: Chuan Yean Tan * Email: tan56@purdue.edu * Date Created: 09/14/2016 -* Description: Translates pipeline bus to a non-pipeline bus +* Description: Translates pipeline bus to a non-pipeline bus */ `include "generic_bus_if.vh" module generic_nonpipeline ( - input logic CLK, nRST, - generic_bus_if pipeline_trans_if, - generic_bus_if.cpu out_gen_bus_if + input logic CLK, + nRST, + generic_bus_if pipeline_trans_if, + generic_bus_if.cpu out_gen_bus_if ); - logic [31:0] next_wdata; - logic next_busy; + logic [31:0] next_wdata; + logic next_busy; - always_ff @ ( posedge CLK, negedge nRST ) - begin - if(!nRST) - begin - out_gen_bus_if.wen <= pipeline_trans_if.wen; - out_gen_bus_if.ren <= pipeline_trans_if.ren; - out_gen_bus_if.addr <= pipeline_trans_if.addr; - out_gen_bus_if.byte_en <= pipeline_trans_if.byte_en; - out_gen_bus_if.wdata <= next_wdata; - end - else - begin - if ((out_gen_bus_if.busy == 0) || (~out_gen_bus_if.ren && ~out_gen_bus_if.wen)) - begin - out_gen_bus_if.wen <= pipeline_trans_if.wen; - out_gen_bus_if.ren <= pipeline_trans_if.ren; - out_gen_bus_if.addr <= pipeline_trans_if.addr; - out_gen_bus_if.byte_en <= pipeline_trans_if.byte_en; - out_gen_bus_if.wdata <= next_wdata; - end - end - end + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) begin + out_gen_bus_if.wen <= pipeline_trans_if.wen; + out_gen_bus_if.ren <= pipeline_trans_if.ren; + out_gen_bus_if.addr <= pipeline_trans_if.addr; + out_gen_bus_if.byte_en <= pipeline_trans_if.byte_en; + out_gen_bus_if.wdata <= next_wdata; + end else begin + if ((out_gen_bus_if.busy == 0) || (~out_gen_bus_if.ren && ~out_gen_bus_if.wen)) begin + out_gen_bus_if.wen <= pipeline_trans_if.wen; + out_gen_bus_if.ren <= pipeline_trans_if.ren; + out_gen_bus_if.addr <= pipeline_trans_if.addr; + out_gen_bus_if.byte_en <= pipeline_trans_if.byte_en; + out_gen_bus_if.wdata <= next_wdata; + end + end + end - assign pipeline_trans_if.busy = (out_gen_bus_if.addr == 0) ? 1 : out_gen_bus_if.busy; - assign pipeline_trans_if.rdata = out_gen_bus_if.rdata; - assign next_wdata = pipeline_trans_if.wdata; + assign pipeline_trans_if.busy = (out_gen_bus_if.addr == 0) ? 1 : out_gen_bus_if.busy; + assign pipeline_trans_if.rdata = out_gen_bus_if.rdata; + assign next_wdata = pipeline_trans_if.wdata; endmodule diff --git a/source_code/caches/caches.core b/source_code/caches/caches.core new file mode 100644 index 000000000..5e4f616e2 --- /dev/null +++ b/source_code/caches/caches.core @@ -0,0 +1,20 @@ +CAPI=2: +name: socet:riscv:caches:0.1.0 +description: caches + +filesets: + rtl: + depend: + - "socet:riscv:packages" + files: + - caches_wrapper.sv + - separate_caches.sv + - direct_mapped_tpf/direct_mapped_tpf_cache.sv + - pass_through/pass_through_cache.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + toplevel: caches_wrapper \ No newline at end of file diff --git a/source_code/caches/caches_wrapper.sv b/source_code/caches/caches_wrapper.sv index 025019160..11ab9b126 100644 --- a/source_code/caches/caches_wrapper.sv +++ b/source_code/caches/caches_wrapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,26 +19,27 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 11/08/2016 -* Description: Wrapper file that instantiates the desired cache structure +* Description: Wrapper file that instantiates the desired cache structure */ `include "generic_bus_if.vh" `include "cache_control_if.vh" `include "component_selection_defines.vh" -module caches_wrapper( - input logic CLK, nRST, - generic_bus_if.cpu icache_mem_gen_bus_if, - generic_bus_if.cpu dcache_mem_gen_bus_if, - generic_bus_if.generic_bus icache_proc_gen_bus_if, - generic_bus_if.generic_bus dcache_proc_gen_bus_if, - cache_control_if cc_if +module caches_wrapper ( + input logic CLK, + nRST, + generic_bus_if.cpu icache_mem_gen_bus_if, + generic_bus_if.cpu dcache_mem_gen_bus_if, + generic_bus_if.generic_bus icache_proc_gen_bus_if, + generic_bus_if.generic_bus dcache_proc_gen_bus_if, + cache_control_if cc_if ); - generate - case (CACHE_CONFIG) - "separate" : separate_caches sep_caches(.*); - endcase - endgenerate + generate + case (CACHE_CONFIG) + "separate": separate_caches sep_caches (.*); + endcase + endgenerate endmodule - + diff --git a/source_code/caches/direct_mapped_tpf/direct_mapped_tpf_cache.sv b/source_code/caches/direct_mapped_tpf/direct_mapped_tpf_cache.sv index 9afb79dea..dc3222fea 100644 --- a/source_code/caches/direct_mapped_tpf/direct_mapped_tpf_cache.sv +++ b/source_code/caches/direct_mapped_tpf/direct_mapped_tpf_cache.sv @@ -1,528 +1,496 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: direct_mapped_tpf_cache.sv -* +* * Created by: John Skubic -* Email: jjs.skubic@gmail.com +* Email: jjs.skubic@gmail.com * Date Created: 05/28/2017 * Description: Direct Mapped Cache with tagged prefetch. The following are configurable: * - Cache Size * - Non-Cacheable start address * - Block Size -* - Prefetch Length +* - Prefetch Length */ `include "generic_bus_if.vh" module direct_mapped_tpf_cache ( - input logic CLK, nRST, - input logic clear, flush, - output logic clear_done, flush_done, - generic_bus_if.cpu mem_gen_bus_if, - generic_bus_if.generic_bus proc_gen_bus_if + input logic CLK, + nRST, + input logic clear, + flush, + output logic clear_done, + flush_done, + generic_bus_if.cpu mem_gen_bus_if, + generic_bus_if.generic_bus proc_gen_bus_if ); - import rv32i_types_pkg::*; - - /* --- Parameters --- */ - - // configurable parameters - parameter CACHE_SIZE = 1024; // In bytes, must be power of 2 - parameter BLOCK_SIZE = 2; // must be power of 2 - parameter PREFETCH_LENGTH = 1; // must be power of 2 - parameter NONCACHE_START_ADDR = 32'h8000_0000; - - // local parameters - localparam N_INDICES = CACHE_SIZE / (BLOCK_SIZE * WORD_SIZE / 8); - localparam BLK_OFF_BITS = $clog2(BLOCK_SIZE); - localparam IDX_BITS = $clog2(N_INDICES); - localparam TAG_BITS = RAM_ADDR_SIZE - 2 - BLK_OFF_BITS - IDX_BITS; - localparam N_BITS_IN_FRAME = (BLOCK_SIZE * WORD_SIZE) + TAG_BITS + 3; - localparam N_BYTES_IN_FRAME = N_BITS_IN_FRAME%8 ? N_BITS_IN_FRAME/8 + 1 : N_BITS_IN_FRAME/8; - localparam META_BYTE_L = (BLOCK_SIZE * WORD_SIZE)/8; - localparam META_BYTE_H = N_BYTES_IN_FRAME-1; - localparam PFETCH_CNT_BITS = $clog2(PREFETCH_LENGTH); - - /* --- Custom Data Types --- */ - - typedef struct packed { - logic [TAG_BITS-1:0] tag; - logic [IDX_BITS-1:0] idx; - logic [BLK_OFF_BITS-1:0] blk_off; - logic [1:0] byte_off; - } cache_addr_t; - - typedef struct packed { - logic d; - logic v; - logic p; - logic [TAG_BITS-1:0] tag; - word_t [BLOCK_SIZE-1:0] data; - } cache_frame_t; - - typedef enum logic [3:0] { - IDLE, - // flush and clear handling - CLEAR_PREP, - CLEAR_WB, - CLEAR_UPDATE, - // normal operation - EVAL, - FETCH, - PREFETCH, - PREFETCH_PREP, - PREFETCH_WB, - WB - } sm_t; - - /* --- Signal Instantiations --- */ - - cache_frame_t frame_buffer, frame_buffer_next; - cache_addr_t req_addr; - - logic init_flag; - logic init_complete; - logic flush_flag; - logic flush_reg; - logic flush_clear; - logic clear_flag, clear_clear, clear_reg; - logic request; - logic direct_mem_req; - logic hit, tag_match; - - logic [3:0] req_byte_en; - logic [31:0] req_byte_en_expand; - - cache_addr_t curr_addr, curr_addr_next; - - // signals for accessing the sram of cache - cache_frame_t cache_wdata; - logic [RAM_ADDR_SIZE-1:0] cache_addr; - logic [N_BYTES_IN_FRAME-1:0] cache_byte_en; - logic cache_wen, cache_ren; - logic [N_BITS_IN_FRAME-1:0] cache_rdata; - logic cache_busy, cache_busy_raw; - - // state machine - sm_t curr_state, next_state; - cache_addr_t sm_addr; - generic_bus_if sm_bus_if(); - - logic [IDX_BITS:0] flush_cnt, flush_cnt_next; - logic [BLK_OFF_BITS:0] access_cnt, access_cnt_next; - logic [PFETCH_CNT_BITS:0] prefetch_cnt, prefetch_cnt_next; - - /* --- Module Instantiations --- */ - - // sram for cache - config_ram_wrapper # ( - .N_BYTES(N_BYTES_IN_FRAME), - .DEPTH(N_INDICES) + import rv32i_types_pkg::*; + + /* --- Parameters --- */ + + // configurable parameters + parameter CACHE_SIZE = 1024; // In bytes, must be power of 2 + parameter BLOCK_SIZE = 2; // must be power of 2 + parameter PREFETCH_LENGTH = 1; // must be power of 2 + parameter NONCACHE_START_ADDR = 32'h8000_0000; + + // local parameters + localparam N_INDICES = CACHE_SIZE / (BLOCK_SIZE * WORD_SIZE / 8); + localparam BLK_OFF_BITS = $clog2(BLOCK_SIZE); + localparam IDX_BITS = $clog2(N_INDICES); + localparam TAG_BITS = RAM_ADDR_SIZE - 2 - BLK_OFF_BITS - IDX_BITS; + localparam N_BITS_IN_FRAME = (BLOCK_SIZE * WORD_SIZE) + TAG_BITS + 3; + localparam N_BYTES_IN_FRAME = N_BITS_IN_FRAME%8 ? N_BITS_IN_FRAME/8 + 1 : N_BITS_IN_FRAME/8; + localparam META_BYTE_L = (BLOCK_SIZE * WORD_SIZE) / 8; + localparam META_BYTE_H = N_BYTES_IN_FRAME - 1; + localparam PFETCH_CNT_BITS = $clog2(PREFETCH_LENGTH); + + /* --- Custom Data Types --- */ + + typedef struct packed { + logic [TAG_BITS-1:0] tag; + logic [IDX_BITS-1:0] idx; + logic [BLK_OFF_BITS-1:0] blk_off; + logic [1:0] byte_off; + } cache_addr_t; + + typedef struct packed { + logic d; + logic v; + logic p; + logic [TAG_BITS-1:0] tag; + word_t [BLOCK_SIZE-1:0] data; + } cache_frame_t; + + typedef enum logic [3:0] { + IDLE, + // flush and clear handling + CLEAR_PREP, + CLEAR_WB, + CLEAR_UPDATE, + // normal operation + EVAL, + FETCH, + PREFETCH, + PREFETCH_PREP, + PREFETCH_WB, + WB + } sm_t; + + /* --- Signal Instantiations --- */ + + cache_frame_t frame_buffer, frame_buffer_next; + cache_addr_t req_addr; + + logic init_flag; + logic init_complete; + logic flush_flag; + logic flush_reg; + logic flush_clear; + logic clear_flag, clear_clear, clear_reg; + logic request; + logic direct_mem_req; + logic hit, tag_match; + + logic [ 3:0] req_byte_en; + logic [31:0] req_byte_en_expand; + + cache_addr_t curr_addr, curr_addr_next; + + // signals for accessing the sram of cache + cache_frame_t cache_wdata; + logic [RAM_ADDR_SIZE-1:0] cache_addr; + logic [N_BYTES_IN_FRAME-1:0] cache_byte_en; + logic cache_wen, cache_ren; + logic [N_BITS_IN_FRAME-1:0] cache_rdata; + logic cache_busy, cache_busy_raw; + + // state machine + sm_t curr_state, next_state; + cache_addr_t sm_addr; + generic_bus_if sm_bus_if (); + + logic [IDX_BITS:0] flush_cnt, flush_cnt_next; + logic [BLK_OFF_BITS:0] access_cnt, access_cnt_next; + logic [PFETCH_CNT_BITS:0] prefetch_cnt, prefetch_cnt_next; + + /* --- Module Instantiations --- */ + + // sram for cache + config_ram_wrapper #( + .N_BYTES(N_BYTES_IN_FRAME), + .DEPTH (N_INDICES) ) cache_mem ( - .CLK(CLK), - .nRST(nRST), - .wdata(cache_wdata), - .addr(cache_addr), - .byte_en(cache_byte_en), - .wen(cache_wen), - .ren(cache_ren), - .rdata(cache_rdata), - .busy(cache_busy_raw) - ); - - assign cache_busy = cache_busy_raw | ~(cache_wen | cache_ren); - - /* --- Glue Logic and Output Logic --- */ - - assign req_addr = proc_gen_bus_if.addr; - assign req_byte_en = proc_gen_bus_if.byte_en; - assign req_byte_en_expand = {{8{req_byte_en[3]}}, {8{req_byte_en[2]}}, {8{req_byte_en[1]}}, {8{req_byte_en[0]}}}; - assign direct_mem_req = (proc_gen_bus_if.addr >= NONCACHE_START_ADDR); - assign flush_flag = flush | init_flag | flush_reg; - assign clear_flag = clear | clear_reg; - assign request = (proc_gen_bus_if.wen | proc_gen_bus_if.ren) && ~direct_mem_req; - - // clear and flush response - assign clear_done = clear_clear; - assign flush_done = flush_clear; - - //hits given out in EVAL and FETCH - assign tag_match = (req_addr.tag == frame_buffer.tag) && frame_buffer.v; - always_comb begin - hit = 1'b0; - if (curr_state == EVAL) begin - if (tag_match && (~proc_gen_bus_if.wen || ~cache_busy)) - hit = 1'b1; - end else if ((curr_state == FETCH) && (access_cnt == BLOCK_SIZE) && ~cache_busy) begin - hit = 1'b1; + .CLK(CLK), + .nRST(nRST), + .wdata(cache_wdata), + .addr(cache_addr), + .byte_en(cache_byte_en), + .wen(cache_wen), + .ren(cache_ren), + .rdata(cache_rdata), + .busy(cache_busy_raw) + ); + + assign cache_busy = cache_busy_raw | ~(cache_wen | cache_ren); + + /* --- Glue Logic and Output Logic --- */ + + assign req_addr = proc_gen_bus_if.addr; + assign req_byte_en = proc_gen_bus_if.byte_en; + assign req_byte_en_expand = { + {8{req_byte_en[3]}}, {8{req_byte_en[2]}}, {8{req_byte_en[1]}}, {8{req_byte_en[0]}} + }; + assign direct_mem_req = (proc_gen_bus_if.addr >= NONCACHE_START_ADDR); + assign flush_flag = flush | init_flag | flush_reg; + assign clear_flag = clear | clear_reg; + assign request = (proc_gen_bus_if.wen | proc_gen_bus_if.ren) && ~direct_mem_req; + + // clear and flush response + assign clear_done = clear_clear; + assign flush_done = flush_clear; + + //hits given out in EVAL and FETCH + assign tag_match = (req_addr.tag == frame_buffer.tag) && frame_buffer.v; + always_comb begin + hit = 1'b0; + if (curr_state == EVAL) begin + if (tag_match && (~proc_gen_bus_if.wen || ~cache_busy)) hit = 1'b1; + end else if ((curr_state == FETCH) && (access_cnt == BLOCK_SIZE) && ~cache_busy) begin + hit = 1'b1; + end end - end - - // Memory Arbitration between state machine and direct memory access - assign mem_gen_bus_if.addr = direct_mem_req ? proc_gen_bus_if.addr : sm_bus_if.addr; - assign mem_gen_bus_if.wdata = direct_mem_req ? proc_gen_bus_if.wdata : sm_bus_if.wdata; - assign mem_gen_bus_if.ren = direct_mem_req ? proc_gen_bus_if.ren : sm_bus_if.ren; - assign mem_gen_bus_if.wen = direct_mem_req ? proc_gen_bus_if.wen : sm_bus_if.wen; - assign mem_gen_bus_if.byte_en = direct_mem_req ? proc_gen_bus_if.byte_en : sm_bus_if.byte_en; - assign proc_gen_bus_if.rdata = direct_mem_req ? mem_gen_bus_if.rdata :frame_buffer.data[req_addr.blk_off] ; - assign sm_bus_if.rdata = mem_gen_bus_if.rdata; - assign proc_gen_bus_if.busy = direct_mem_req ? mem_gen_bus_if.busy : ~hit; - assign sm_bus_if.busy = direct_mem_req ? 1'b1 : mem_gen_bus_if.busy; - - assign sm_bus_if.addr = sm_addr; - - - // Flip-Flops with no reset - always_ff @ (posedge CLK) begin - frame_buffer <= frame_buffer_next; - curr_addr <= curr_addr_next; - end - - // Flip-Flops with reset - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) begin - flush_cnt <= '0; - access_cnt <= '0; - prefetch_cnt <= '0; - end else begin - flush_cnt <= flush_cnt_next; - access_cnt <= access_cnt_next; - prefetch_cnt <= prefetch_cnt_next; + + // Memory Arbitration between state machine and direct memory access + assign mem_gen_bus_if.addr = direct_mem_req ? proc_gen_bus_if.addr : sm_bus_if.addr; + assign mem_gen_bus_if.wdata = direct_mem_req ? proc_gen_bus_if.wdata : sm_bus_if.wdata; + assign mem_gen_bus_if.ren = direct_mem_req ? proc_gen_bus_if.ren : sm_bus_if.ren; + assign mem_gen_bus_if.wen = direct_mem_req ? proc_gen_bus_if.wen : sm_bus_if.wen; + assign mem_gen_bus_if.byte_en = direct_mem_req ? proc_gen_bus_if.byte_en : sm_bus_if.byte_en; + assign proc_gen_bus_if.rdata = direct_mem_req ? mem_gen_bus_if.rdata :frame_buffer.data[req_addr.blk_off] ; + assign sm_bus_if.rdata = mem_gen_bus_if.rdata; + assign proc_gen_bus_if.busy = direct_mem_req ? mem_gen_bus_if.busy : ~hit; + assign sm_bus_if.busy = direct_mem_req ? 1'b1 : mem_gen_bus_if.busy; + + assign sm_bus_if.addr = sm_addr; + + + // Flip-Flops with no reset + always_ff @(posedge CLK) begin + frame_buffer <= frame_buffer_next; + curr_addr <= curr_addr_next; end - end - - // Init on reset - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - init_flag <= 1'b1; - else if (init_complete) - init_flag <= 1'b0; - end - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - flush_reg <= 1'b0; - else if (flush_clear) - flush_reg <= 1'b0; - else if (flush) - flush_reg <= 1'b1; - end - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - clear_reg <= 1'b0; - else if (clear_clear) - clear_reg <= 1'b0; - else if (clear) - clear_reg <= 1'b1; - end - - // SM ouptut Logic - - always_comb begin - flush_cnt_next = flush_cnt; - cache_addr = '0; - cache_ren = 0; - cache_wen = 0; - cache_byte_en = '1; - cache_wdata = '0; - curr_addr_next = curr_addr; - frame_buffer_next = frame_buffer; - access_cnt_next = access_cnt; - prefetch_cnt_next = prefetch_cnt; - sm_addr = '0; - sm_bus_if.wen = 0; - sm_bus_if.ren = 0; - sm_bus_if.wdata = '0; - sm_bus_if.byte_en = '1; - init_complete = 1'b0; - flush_clear = 1'b0; - clear_clear = 1'b0; - - casez (curr_state) - IDLE : begin - if (clear_flag) begin - curr_addr_next = req_addr; - flush_cnt_next = N_INDICES-1; - end else if (flush_flag) begin - curr_addr_next = 0; - flush_cnt_next = 0; - end else if (request) begin - cache_addr = req_addr.idx; - cache_ren = 1'b1; - if(~cache_busy) begin - curr_addr_next = req_addr; - frame_buffer_next = cache_rdata; - end + + // Flip-Flops with reset + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + flush_cnt <= '0; + access_cnt <= '0; + prefetch_cnt <= '0; + end else begin + flush_cnt <= flush_cnt_next; + access_cnt <= access_cnt_next; + prefetch_cnt <= prefetch_cnt_next; end - end - // Normal Operation - EVAL : begin - if (~tag_match) - access_cnt_next = 0; - else begin // hit - if (hit && frame_buffer.p) begin - {curr_addr_next.tag, curr_addr_next.idx} = + end + + // Init on reset + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) init_flag <= 1'b1; + else if (init_complete) init_flag <= 1'b0; + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) flush_reg <= 1'b0; + else if (flush_clear) flush_reg <= 1'b0; + else if (flush) flush_reg <= 1'b1; + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) clear_reg <= 1'b0; + else if (clear_clear) clear_reg <= 1'b0; + else if (clear) clear_reg <= 1'b1; + end + + // SM ouptut Logic + + always_comb begin + flush_cnt_next = flush_cnt; + cache_addr = '0; + cache_ren = 0; + cache_wen = 0; + cache_byte_en = '1; + cache_wdata = '0; + curr_addr_next = curr_addr; + frame_buffer_next = frame_buffer; + access_cnt_next = access_cnt; + prefetch_cnt_next = prefetch_cnt; + sm_addr = '0; + sm_bus_if.wen = 0; + sm_bus_if.ren = 0; + sm_bus_if.wdata = '0; + sm_bus_if.byte_en = '1; + init_complete = 1'b0; + flush_clear = 1'b0; + clear_clear = 1'b0; + + casez (curr_state) + IDLE: begin + if (clear_flag) begin + curr_addr_next = req_addr; + flush_cnt_next = N_INDICES - 1; + end else if (flush_flag) begin + curr_addr_next = 0; + flush_cnt_next = 0; + end else if (request) begin + cache_addr = req_addr.idx; + cache_ren = 1'b1; + if (~cache_busy) begin + curr_addr_next = req_addr; + frame_buffer_next = cache_rdata; + end + end + end + // Normal Operation + EVAL: begin + if (~tag_match) access_cnt_next = 0; + else begin // hit + if (hit && frame_buffer.p) begin + {curr_addr_next.tag, curr_addr_next.idx} = {curr_addr.tag, curr_addr.idx} + 1; - prefetch_cnt_next = 0; - end - if (proc_gen_bus_if.wen) begin // cache write hit - cache_addr = req_addr.idx; - frame_buffer_next.data[req_addr.blk_off] = + prefetch_cnt_next = 0; + end + if (proc_gen_bus_if.wen) begin // cache write hit + cache_addr = req_addr.idx; + frame_buffer_next.data[req_addr.blk_off] = (frame_buffer.data[req_addr.blk_off] & ~req_byte_en_expand) | (req_byte_en_expand & proc_gen_bus_if.wdata); - frame_buffer_next.d = 1'b1; - cache_wdata = frame_buffer_next; - cache_wdata.p = 1'b0; - cache_wen = 1'b1; - end - end - end - FETCH : begin - sm_addr = curr_addr; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - - cache_wdata = frame_buffer; - cache_addr = curr_addr.idx; - if (access_cnt == BLOCK_SIZE) begin - sm_bus_if.ren = 1'b0; - cache_wen = 1'b1; - if (~cache_busy) begin - {curr_addr_next.tag, curr_addr_next.idx} + frame_buffer_next.d = 1'b1; + cache_wdata = frame_buffer_next; + cache_wdata.p = 1'b0; + cache_wen = 1'b1; + end + end + end + FETCH: begin + sm_addr = curr_addr; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + + cache_wdata = frame_buffer; + cache_addr = curr_addr.idx; + if (access_cnt == BLOCK_SIZE) begin + sm_bus_if.ren = 1'b0; + cache_wen = 1'b1; + if (~cache_busy) begin + {curr_addr_next.tag, curr_addr_next.idx} = {curr_addr.tag, curr_addr.idx} + 1; - prefetch_cnt_next = 0; - end - end else begin - sm_bus_if.ren = 1'b1; - cache_wen = 1'b0; - end - - frame_buffer_next.tag = req_addr.tag; - frame_buffer_next.p = 0; - frame_buffer_next.v = 1; - frame_buffer_next.d = proc_gen_bus_if.wen; - - if (~sm_bus_if.busy) begin - if (proc_gen_bus_if.wen && (sm_addr.blk_off == req_addr.blk_off)) begin - frame_buffer_next.data[sm_addr.blk_off] = + prefetch_cnt_next = 0; + end + end else begin + sm_bus_if.ren = 1'b1; + cache_wen = 1'b0; + end + + frame_buffer_next.tag = req_addr.tag; + frame_buffer_next.p = 0; + frame_buffer_next.v = 1; + frame_buffer_next.d = proc_gen_bus_if.wen; + + if (~sm_bus_if.busy) begin + if (proc_gen_bus_if.wen && (sm_addr.blk_off == req_addr.blk_off)) begin + frame_buffer_next.data[sm_addr.blk_off] = (sm_bus_if.rdata & ~req_byte_en_expand) | (req_byte_en_expand & proc_gen_bus_if.wdata); - end else begin - frame_buffer_next.data[sm_addr.blk_off] = sm_bus_if.rdata; - end - access_cnt_next = access_cnt + 1; - end - end - WB : begin - sm_addr.idx = curr_addr.idx; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - sm_addr.tag = frame_buffer.tag; - sm_bus_if.wdata = frame_buffer.data[access_cnt]; - sm_bus_if.wen = 1'b1; - if (~sm_bus_if.busy) - access_cnt_next = access_cnt + 1; - - if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE-1)) - access_cnt_next = '0; - end - PREFETCH_PREP : begin - cache_addr = curr_addr.idx; - cache_ren = 1'b1; - if (~cache_busy) begin - access_cnt_next = 0; - frame_buffer_next = cache_rdata; - end - end - PREFETCH_WB : begin - sm_addr.idx = curr_addr.idx; - sm_addr.tag = frame_buffer.tag; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - sm_bus_if.wdata = frame_buffer.data[access_cnt]; - sm_bus_if.wen = 1'b1; - sm_bus_if.byte_en = 4'hf; - if (~sm_bus_if.busy) begin - if (access_cnt == BLOCK_SIZE-1) - access_cnt_next = 0; - else - access_cnt_next = access_cnt + 1; - end - end - PREFETCH : begin - sm_addr = curr_addr; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - cache_addr = curr_addr.idx; - cache_wdata = frame_buffer; - - if (access_cnt == BLOCK_SIZE) begin - sm_bus_if.ren = 1'b0; - cache_wen = 1'b1; - end else begin - sm_bus_if.ren = 1'b1; - cache_wen = 1'b0; - end - - if (~sm_bus_if.busy) begin - access_cnt_next = access_cnt + 1; - frame_buffer_next.data[access_cnt] = sm_bus_if.rdata; - frame_buffer_next.p = 1'b1; - frame_buffer_next.v = 1'b1; - frame_buffer_next.d = 1'b0; - frame_buffer_next.tag = curr_addr.tag; - end + end else begin + frame_buffer_next.data[sm_addr.blk_off] = sm_bus_if.rdata; + end + access_cnt_next = access_cnt + 1; + end + end + WB: begin + sm_addr.idx = curr_addr.idx; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + sm_addr.tag = frame_buffer.tag; + sm_bus_if.wdata = frame_buffer.data[access_cnt]; + sm_bus_if.wen = 1'b1; + if (~sm_bus_if.busy) access_cnt_next = access_cnt + 1; + + if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE - 1)) access_cnt_next = '0; + end + PREFETCH_PREP: begin + cache_addr = curr_addr.idx; + cache_ren = 1'b1; + if (~cache_busy) begin + access_cnt_next = 0; + frame_buffer_next = cache_rdata; + end + end + PREFETCH_WB: begin + sm_addr.idx = curr_addr.idx; + sm_addr.tag = frame_buffer.tag; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + sm_bus_if.wdata = frame_buffer.data[access_cnt]; + sm_bus_if.wen = 1'b1; + sm_bus_if.byte_en = 4'hf; + if (~sm_bus_if.busy) begin + if (access_cnt == BLOCK_SIZE - 1) access_cnt_next = 0; + else access_cnt_next = access_cnt + 1; + end + end + PREFETCH: begin + sm_addr = curr_addr; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + cache_addr = curr_addr.idx; + cache_wdata = frame_buffer; + + if (access_cnt == BLOCK_SIZE) begin + sm_bus_if.ren = 1'b0; + cache_wen = 1'b1; + end else begin + sm_bus_if.ren = 1'b1; + cache_wen = 1'b0; + end + + if (~sm_bus_if.busy) begin + access_cnt_next = access_cnt + 1; + frame_buffer_next.data[access_cnt] = sm_bus_if.rdata; + frame_buffer_next.p = 1'b1; + frame_buffer_next.v = 1'b1; + frame_buffer_next.d = 1'b0; + frame_buffer_next.tag = curr_addr.tag; + end + + if (~cache_busy) begin + prefetch_cnt_next = prefetch_cnt + 1; + {curr_addr_next.tag, curr_addr_next.idx} = {curr_addr.tag, curr_addr.idx} + 1; + end + end + // Flush and Clear + CLEAR_PREP: begin + cache_addr = curr_addr.idx; + cache_ren = 1'b1; + if (~cache_busy) begin + frame_buffer_next = cache_rdata; + if (frame_buffer_next.d && frame_buffer_next.v) access_cnt_next = 0; + end + end + CLEAR_WB: begin //writeback all words in the current frame + sm_addr.idx = curr_addr.idx; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + sm_addr.tag = frame_buffer.tag; + sm_bus_if.wdata = frame_buffer.data[access_cnt]; + sm_bus_if.wen = 1'b1; + if (~sm_bus_if.busy) access_cnt_next = access_cnt + 1; + end + CLEAR_UPDATE: begin // clear out the cache memory, increment flush count + cache_addr = curr_addr.idx; + cache_wen = 1'b1; + cache_wdata = '0; + cache_byte_en = '1; + if (~cache_busy) begin + flush_cnt_next = flush_cnt + 1; + curr_addr_next.idx = curr_addr.idx + 1; + if (flush_cnt == N_INDICES - 1) begin + init_complete = 1'b1; + if (flush_reg) flush_clear = 1'b1; + else if (clear_reg) clear_clear = 1'b1; + end + end + end + endcase + end - if (~cache_busy) begin - prefetch_cnt_next = prefetch_cnt + 1; - {curr_addr_next.tag, curr_addr_next.idx} - = {curr_addr.tag, curr_addr.idx} + 1; - end - end - // Flush and Clear - CLEAR_PREP : begin - cache_addr = curr_addr.idx; - cache_ren = 1'b1; - if (~cache_busy) begin - frame_buffer_next = cache_rdata; - if (frame_buffer_next.d && frame_buffer_next.v) - access_cnt_next = 0; - end - end - CLEAR_WB : begin //writeback all words in the current frame - sm_addr.idx = curr_addr.idx; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - sm_addr.tag = frame_buffer.tag; - sm_bus_if.wdata = frame_buffer.data[access_cnt]; - sm_bus_if.wen = 1'b1; - if (~sm_bus_if.busy) - access_cnt_next = access_cnt + 1; - end - CLEAR_UPDATE : begin // clear out the cache memory, increment flush count - cache_addr = curr_addr.idx; - cache_wen = 1'b1; - cache_wdata = '0; - cache_byte_en = '1; - if (~cache_busy) begin - flush_cnt_next = flush_cnt + 1; - curr_addr_next.idx = curr_addr.idx + 1; - if (flush_cnt == N_INDICES-1) begin - init_complete = 1'b1; - if (flush_reg) - flush_clear = 1'b1; - else if (clear_reg) - clear_clear = 1'b1; - end - end - end - endcase - end - - /* --- State Machine Logic --- */ - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - curr_state <= IDLE; - else - curr_state <= next_state; - end - - // Next State Logic - - always_comb begin - next_state = curr_state; - casez (curr_state) - IDLE : begin - if (clear_flag || flush_flag) - next_state = CLEAR_PREP; - else if (~cache_busy && request) begin - next_state = EVAL; - end - end - // Normal Operation - EVAL : begin - if (proc_gen_bus_if.ren && tag_match) begin - if (frame_buffer.p) - next_state = PREFETCH_PREP; - else - next_state = IDLE; - end else if (proc_gen_bus_if.wen && tag_match) begin - if (~cache_busy) begin - if (frame_buffer.p) - next_state = PREFETCH_PREP; - else - next_state = IDLE; - end - end else if (frame_buffer.v && frame_buffer.d) - next_state = WB; - else - next_state = FETCH; - end - FETCH : begin - if ((access_cnt == BLOCK_SIZE) && ~cache_busy) - next_state = PREFETCH_PREP; - end - PREFETCH_PREP : begin - if (~cache_busy) begin - if (frame_buffer_next.d && frame_buffer_next.v) - next_state = PREFETCH_WB; - else - next_state = PREFETCH; - end - end - PREFETCH_WB : begin - if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE-1)) - next_state = PREFETCH; - end - PREFETCH : begin - if ((access_cnt == BLOCK_SIZE) && ~cache_busy) begin - if (prefetch_cnt == PREFETCH_LENGTH-1) - next_state = IDLE; - else - next_state = PREFETCH_PREP; - end - end - WB : begin - if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE-1)) - next_state = FETCH; - end - // Flush and Clear - CLEAR_PREP : begin - if (~cache_busy) begin - if (~init_flag && frame_buffer_next.d && frame_buffer_next.v) - next_state = CLEAR_WB; - else - next_state = CLEAR_UPDATE; - end - end - CLEAR_WB : begin - if (~sm_bus_if.busy && (access_cnt == (BLOCK_SIZE-1))) - next_state = CLEAR_UPDATE; - end - CLEAR_UPDATE : begin - if (~cache_busy) begin - if (flush_cnt != N_INDICES-1) - next_state = CLEAR_PREP; - else - next_state = IDLE; - end - end - endcase - end + /* --- State Machine Logic --- */ + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) curr_state <= IDLE; + else curr_state <= next_state; + end + + // Next State Logic + + always_comb begin + next_state = curr_state; + casez (curr_state) + IDLE: begin + if (clear_flag || flush_flag) next_state = CLEAR_PREP; + else if (~cache_busy && request) begin + next_state = EVAL; + end + end + // Normal Operation + EVAL: begin + if (proc_gen_bus_if.ren && tag_match) begin + if (frame_buffer.p) next_state = PREFETCH_PREP; + else next_state = IDLE; + end else if (proc_gen_bus_if.wen && tag_match) begin + if (~cache_busy) begin + if (frame_buffer.p) next_state = PREFETCH_PREP; + else next_state = IDLE; + end + end else if (frame_buffer.v && frame_buffer.d) next_state = WB; + else next_state = FETCH; + end + FETCH: begin + if ((access_cnt == BLOCK_SIZE) && ~cache_busy) next_state = PREFETCH_PREP; + end + PREFETCH_PREP: begin + if (~cache_busy) begin + if (frame_buffer_next.d && frame_buffer_next.v) next_state = PREFETCH_WB; + else next_state = PREFETCH; + end + end + PREFETCH_WB: begin + if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE - 1)) next_state = PREFETCH; + end + PREFETCH: begin + if ((access_cnt == BLOCK_SIZE) && ~cache_busy) begin + if (prefetch_cnt == PREFETCH_LENGTH - 1) next_state = IDLE; + else next_state = PREFETCH_PREP; + end + end + WB: begin + if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE - 1)) next_state = FETCH; + end + // Flush and Clear + CLEAR_PREP: begin + if (~cache_busy) begin + if (~init_flag && frame_buffer_next.d && frame_buffer_next.v) + next_state = CLEAR_WB; + else next_state = CLEAR_UPDATE; + end + end + CLEAR_WB: begin + if (~sm_bus_if.busy && (access_cnt == (BLOCK_SIZE - 1))) next_state = CLEAR_UPDATE; + end + CLEAR_UPDATE: begin + if (~cache_busy) begin + if (flush_cnt != N_INDICES - 1) next_state = CLEAR_PREP; + else next_state = IDLE; + end + end + + default: next_state = curr_state; + endcase + end endmodule diff --git a/source_code/caches/pass_through/pass_through_cache.sv b/source_code/caches/pass_through/pass_through_cache.sv index ae910ed97..2d590a50c 100644 --- a/source_code/caches/pass_through/pass_through_cache.sv +++ b/source_code/caches/pass_through/pass_through_cache.sv @@ -1,43 +1,44 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: pass_through_cache.sv -* +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 -* Description: Pass Through Cache +* Description: Pass Through Cache */ `include "generic_bus_if.vh" module pass_through_cache ( - input logic CLK, nRST, - generic_bus_if.cpu mem_gen_bus_if, - generic_bus_if.generic_bus proc_gen_bus_if + input logic CLK, + nRST, + generic_bus_if.cpu mem_gen_bus_if, + generic_bus_if.generic_bus proc_gen_bus_if ); - //passthrough layer - assign mem_gen_bus_if.addr = proc_gen_bus_if.addr; - assign mem_gen_bus_if.ren = proc_gen_bus_if.ren; - assign mem_gen_bus_if.wen = proc_gen_bus_if.wen; - assign mem_gen_bus_if.wdata = proc_gen_bus_if.wdata; - assign mem_gen_bus_if.byte_en = proc_gen_bus_if.byte_en; + //passthrough layer + assign mem_gen_bus_if.addr = proc_gen_bus_if.addr; + assign mem_gen_bus_if.ren = proc_gen_bus_if.ren; + assign mem_gen_bus_if.wen = proc_gen_bus_if.wen; + assign mem_gen_bus_if.wdata = proc_gen_bus_if.wdata; + assign mem_gen_bus_if.byte_en = proc_gen_bus_if.byte_en; - assign proc_gen_bus_if.rdata = mem_gen_bus_if.rdata; - assign proc_gen_bus_if.busy = mem_gen_bus_if.busy; + assign proc_gen_bus_if.rdata = mem_gen_bus_if.rdata; + assign proc_gen_bus_if.busy = mem_gen_bus_if.busy; endmodule diff --git a/source_code/caches/separate_caches.sv b/source_code/caches/separate_caches.sv index 760b8be6c..12d2c1eb9 100644 --- a/source_code/caches/separate_caches.sv +++ b/source_code/caches/separate_caches.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 11/08/2016 -* Description: Caches consisting of separate I$ and D$ +* Description: Caches consisting of separate I$ and D$ */ `include "generic_bus_if.vh" @@ -27,65 +27,68 @@ `include "component_selection_defines.vh" module separate_caches ( - input logic CLK, nRST, - generic_bus_if.cpu icache_mem_gen_bus_if, - generic_bus_if.cpu dcache_mem_gen_bus_if, - generic_bus_if.generic_bus icache_proc_gen_bus_if, - generic_bus_if.generic_bus dcache_proc_gen_bus_if, - cache_control_if.caches cc_if + input logic CLK, + nRST, + generic_bus_if.cpu icache_mem_gen_bus_if, + generic_bus_if.cpu dcache_mem_gen_bus_if, + generic_bus_if.generic_bus icache_proc_gen_bus_if, + generic_bus_if.generic_bus dcache_proc_gen_bus_if, + cache_control_if.caches cc_if ); - generate - /* verilator lint_off width */ - case (DCACHE_TYPE) - /* verilator lint_on width */ - "pass_through" : begin - pass_through_cache dcache( - .CLK(CLK), - .nRST(nRST), - .mem_gen_bus_if(dcache_mem_gen_bus_if), - .proc_gen_bus_if(dcache_proc_gen_bus_if) - ); - assign cc_if.dclear_done = 1'b1; - assign cc_if.dflush_done = 1'b1; - end - "direct_mapped_tpf" : direct_mapped_tpf_cache dcache( - .CLK(CLK), - .nRST(nRST), - .mem_gen_bus_if(dcache_mem_gen_bus_if), - .proc_gen_bus_if(dcache_proc_gen_bus_if), - .flush(cc_if.dcache_flush), - .clear(cc_if.dcache_clear), - .flush_done(cc_if.dflush_done), - .clear_done(cc_if.dclear_done) - ); - endcase - endgenerate + generate + /* verilator lint_off width */ + case (DCACHE_TYPE) + /* verilator lint_on width */ + "pass_through": begin : g_dcache_passthrough + pass_through_cache dcache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(dcache_mem_gen_bus_if), + .proc_gen_bus_if(dcache_proc_gen_bus_if) + ); + assign cc_if.dclear_done = 1'b1; + assign cc_if.dflush_done = 1'b1; + end + "direct_mapped_tpf": + direct_mapped_tpf_cache dcache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(dcache_mem_gen_bus_if), + .proc_gen_bus_if(dcache_proc_gen_bus_if), + .flush(cc_if.dcache_flush), + .clear(cc_if.dcache_clear), + .flush_done(cc_if.dflush_done), + .clear_done(cc_if.dclear_done) + ); + endcase + endgenerate - generate - /* verilator lint_off width */ - case (ICACHE_TYPE) - /* verilator lint_on width */ - "pass_through" : begin - pass_through_cache icache( - .CLK(CLK), - .nRST(nRST), - .mem_gen_bus_if(icache_mem_gen_bus_if), - .proc_gen_bus_if(icache_proc_gen_bus_if) - ); - assign cc_if.iclear_done = 1'b1; - assign cc_if.iflush_done = 1'b1; - end - "direct_mapped_tpf" : direct_mapped_tpf_cache icache( - .CLK(CLK), - .nRST(nRST), - .mem_gen_bus_if(icache_mem_gen_bus_if), - .proc_gen_bus_if(icache_proc_gen_bus_if), - .flush(cc_if.icache_flush), - .clear(cc_if.icache_clear), - .flush_done(cc_if.iflush_done), - .clear_done(cc_if.iclear_done) - ); - endcase - endgenerate + generate + /* verilator lint_off width */ + case (ICACHE_TYPE) + /* verilator lint_on width */ + "pass_through": begin : g_icache_passthrough + pass_through_cache icache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(icache_mem_gen_bus_if), + .proc_gen_bus_if(icache_proc_gen_bus_if) + ); + assign cc_if.iclear_done = 1'b1; + assign cc_if.iflush_done = 1'b1; + end + "direct_mapped_tpf": + direct_mapped_tpf_cache icache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(icache_mem_gen_bus_if), + .proc_gen_bus_if(icache_proc_gen_bus_if), + .flush(cc_if.icache_flush), + .clear(cc_if.icache_clear), + .flush_done(cc_if.iflush_done), + .clear_done(cc_if.iclear_done) + ); + endcase + endgenerate endmodule diff --git a/source_code/caches/wscript b/source_code/caches/wscript deleted file mode 100644 index 76cc3a017..000000000 --- a/source_code/caches/wscript +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(pass_through) - cnf.recurse(direct_mapped_tpf) - -def sim_source(cnf): - cnf.recurse(pass_through) - cnf.recurse(direct_mapped_tpf) diff --git a/source_code/fpga/RISCVBusiness_fpga.sv b/source_code/fpga/RISCVBusiness_fpga.sv index b9c331fc9..a3c36353c 100644 --- a/source_code/fpga/RISCVBusiness_fpga.sv +++ b/source_code/fpga/RISCVBusiness_fpga.sv @@ -103,7 +103,7 @@ module RISCVBusiness_fpga assign HEX6 = ~display.hex6; assign HEX7 = ~display.hex7; - generate + generate genvar seg_select; for(seg_select=0; seg_select < 8; seg_select= seg_select + 1) begin: seven_seg_display_controller diff --git a/source_code/include/ahb_if.vh b/source_code/include/ahb_if.vh deleted file mode 100644 index f16b176f0..000000000 --- a/source_code/include/ahb_if.vh +++ /dev/null @@ -1,40 +0,0 @@ -// File name: ahb_if.vh -// Created: 9/10/2015 -// Author: Erin Rasmussen -// Version 1.0 -// Description: Interface for fully specified AMBA 3 AHB-Lite - -`ifndef AHB_IF_VH -`define AHB_IF_VH - -interface ahb_if; - logic [1:0] HTRANS; - logic [1:0] HRESP; - //logic [1:0] HRESP; JOHN CHANGED THIS - logic [2:0] HSIZE; - logic [31:0] HADDR; - logic [31:0] HWDATA; - logic HWRITE, HREADY,HREADYOUT, HSEL, HMASTLOCK; - logic [31:0] HRDATA; - logic [2:0] HBURST; - logic [3:0] HPROT; - - modport ahb_s ( - input HTRANS, HWRITE, HADDR, HWDATA, HSIZE, HSEL, HBURST, HREADY, - HPROT, HMASTLOCK, - output HREADYOUT, HRESP, HRDATA - ); - - modport ahb_m ( - input HREADY, HRESP, HRDATA, - output HTRANS, HWRITE, HADDR, HWDATA, HSIZE, HBURST, HPROT, HMASTLOCK - ); - - modport ahb_m_i ( - output HREADY, HRESP, HRDATA, - input HTRANS, HWRITE, HADDR, HWDATA, HSIZE, HBURST, HPROT, HMASTLOCK - ); - -endinterface // ahb_if - -`endif // `ifndef AHB_IF_VH diff --git a/source_code/include/include.core b/source_code/include/include.core new file mode 100644 index 000000000..2b9b40ad2 --- /dev/null +++ b/source_code/include/include.core @@ -0,0 +1,37 @@ +CAPI=2: +name: socet:riscv:riscv_include:0.1.0 +description: include files + +filesets: + rtl: + files: + - cache_control_if.vh : {is_include_file: true} + - component_selection_defines.vh : {is_include_file: true} + - risc_mgmt_if.vh : {is_include_file: true} + - generic_bus_if.vh : {is_include_file: true} + - rv32c_if.vh : {is_include_file: true} + - risc_mgmt_decode_if.vh : {is_include_file: true} + - risc_mgmt_execute_if.vh : {is_include_file: true} + - risc_mgmt_macros.vh : {is_include_file: true} + - risc_mgmt_memory_if.vh : {is_include_file: true} + - tspp_fetch_execute_if.vh : {is_include_file: true} + - tspp_hazard_unit_if.vh : {is_include_file: true} + - control_unit_if.vh : {is_include_file: true} + - jump_calc_if.vh : {is_include_file: true} + - priv_1_12_internal_if.vh : {is_include_file: true} + - priv_ext_if.vh : {is_include_file: true} + - prv_pipeline_if.vh : {is_include_file: true} + - rv32i_reg_file_if.vh : {is_include_file: true} + - predictor_pipeline_if.vh : {is_include_file: true} + - alu_if.vh : {is_include_file: true} + - branch_res_if.vh : {is_include_file: true} + - core_interrupt_if.vh : {is_include_file: true} + - decompressor_if.vh : {is_include_file: true} + - fetch_buffer_if.vh : {is_include_file: true} + - sparce_pipeline_if.vh : {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl diff --git a/source_code/include/priv_1_12_internal_if.vh b/source_code/include/priv_1_12_internal_if.vh index 4542efeb7..0cddc0219 100644 --- a/source_code/include/priv_1_12_internal_if.vh +++ b/source_code/include/priv_1_12_internal_if.vh @@ -84,7 +84,7 @@ interface priv_1_12_internal_if; inject_mcause, inject_mepc, inject_mie, inject_mip, inject_mstatus, inject_mtval, next_mcause, next_mepc, next_mie, next_mip, next_mstatus, next_mtval, output old_csr_val, invalid_csr, - curr_mcause, curr_mepc, curr_mie, curr_mip, curr_mstatus, curr_mtval, curr_mtvec + curr_mcause, curr_mepc, curr_mie, curr_mip, curr_mstatus, curr_mtvec ); modport int_ex_handler ( diff --git a/source_code/packages/alu_types_pkg.sv b/source_code/packages/alu_types_pkg.sv index d0fd74a7b..122da19d6 100644 --- a/source_code/packages/alu_types_pkg.sv +++ b/source_code/packages/alu_types_pkg.sv @@ -1,22 +1,22 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: alu_types_pkg.sv -* -* Created by: Jacob R. Stevens +* +* Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 06/01/2016 * Description: Package containing types used in the alu @@ -26,18 +26,18 @@ package alu_types_pkg; - typedef enum logic [3:0] { - ALU_SLL = 4'b0000, - ALU_SRL = 4'b0001, - ALU_SRA = 4'b0010, - ALU_ADD = 4'b0011, - ALU_SUB = 4'b0100, - ALU_AND = 4'b0101, - ALU_OR = 4'b0110, - ALU_XOR = 4'b0111, - ALU_SLT = 4'b1000, - ALU_SLTU = 4'b1001 - } aluop_t; + typedef enum logic [3:0] { + ALU_SLL = 4'b0000, + ALU_SRL = 4'b0001, + ALU_SRA = 4'b0010, + ALU_ADD = 4'b0011, + ALU_SUB = 4'b0100, + ALU_AND = 4'b0101, + ALU_OR = 4'b0110, + ALU_XOR = 4'b0111, + ALU_SLT = 4'b1000, + ALU_SLTU = 4'b1001 + } aluop_t; endpackage `endif diff --git a/source_code/packages/machine_mode_types_1_11_pkg.sv b/source_code/packages/machine_mode_types_1_11_pkg.sv index 1d44b836d..e184e0040 100644 --- a/source_code/packages/machine_mode_types_1_11_pkg.sv +++ b/source_code/packages/machine_mode_types_1_11_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,238 +27,238 @@ package machine_mode_types_1_11_pkg; - typedef enum logic [11:0] { - /* Machine Mode Addresses */ - MVENDORID_ADDR = 12'hF11, - MARCHID_ADDR = 12'hF12, - MIMPID_ADDR = 12'hF13, - MHARTID_ADDR = 12'hF14, + typedef enum logic [11:0] { + /* Machine Mode Addresses */ + MVENDORID_ADDR = 12'hF11, + MARCHID_ADDR = 12'hF12, + MIMPID_ADDR = 12'hF13, + MHARTID_ADDR = 12'hF14, + + MSTATUS_ADDR = 12'h300, + MISA_ADDR = 12'h301, + MEDELEG_ADDR = 12'h302, + MIDELEG_ADDR = 12'h303, + MIE_ADDR = 12'h304, + MTVEC_ADDR = 12'h305, + MCOUNTEREN_ADDR = 12'h306, + + MSCRATCH_ADDR = 12'h340, + MEPC_ADDR = 12'h341, + MCAUSE_ADDR = 12'h342, + MTVAL_ADDR = 12'h343, + MIP_ADDR = 12'h344, + + MCYCLE_ADDR = 12'hB00, + MINSTRET_ADDR = 12'hB02, + MCYCLEH_ADDR = 12'hB80, + MINSTRETH_ADDR = 12'hB82 + } csr_addr_t; - MSTATUS_ADDR = 12'h300, - MISA_ADDR = 12'h301, - MEDELEG_ADDR = 12'h302, - MIDELEG_ADDR = 12'h303, - MIE_ADDR = 12'h304, - MTVEC_ADDR = 12'h305, - MCOUNTEREN_ADDR = 12'h306, - - MSCRATCH_ADDR = 12'h340, - MEPC_ADDR = 12'h341, - MCAUSE_ADDR = 12'h342, - MTVAL_ADDR = 12'h343, - MIP_ADDR = 12'h344, + /* Machine Mode Register Types */ - MCYCLE_ADDR = 12'hB00, - MINSTRET_ADDR = 12'hB02, - MCYCLEH_ADDR = 12'hB80, - MINSTRETH_ADDR = 12'hB82 - } csr_addr_t; + /* misaid types */ - /* Machine Mode Register Types */ + typedef enum logic [1:0] { + BASE_RV32 = 2'h1, + BASE_RV64 = 2'h2, + BASE_RV128 = 2'h3 + } misaid_base_t; - /* misaid types */ + typedef struct packed { + misaid_base_t base; + logic [3:0] zero; + logic [25:0] extensions; + } misaid_t; - typedef enum logic [1:0] { - BASE_RV32 = 2'h1, - BASE_RV64 = 2'h2, - BASE_RV128 = 2'h3 - } misaid_base_t; + parameter logic [25:0] MISAID_EXT_A = 26'h1 << 0; + parameter logic [25:0] MISAID_EXT_B = 26'h1 << 1; + parameter logic [25:0] MISAID_EXT_C = 26'h1 << 2; + parameter logic [25:0] MISAID_EXT_D = 26'h1 << 3; + parameter logic [25:0] MISAID_EXT_E = 26'h1 << 4; + parameter logic [25:0] MISAID_EXT_F = 26'h1 << 5; + parameter logic [25:0] MISAID_EXT_G = 26'h1 << 6; + parameter logic [25:0] MISAID_EXT_H = 26'h1 << 7; + parameter logic [25:0] MISAID_EXT_I = 26'h1 << 8; + parameter logic [25:0] MISAID_EXT_J = 26'h1 << 9; + parameter logic [25:0] MISAID_EXT_K = 26'h1 << 10; + parameter logic [25:0] MISAID_EXT_L = 26'h1 << 11; + parameter logic [25:0] MISAID_EXT_M = 26'h1 << 12; + parameter logic [25:0] MISAID_EXT_N = 26'h1 << 13; + parameter logic [25:0] MISAID_EXT_O = 26'h1 << 14; + parameter logic [25:0] MISAID_EXT_P = 26'h1 << 15; + parameter logic [25:0] MISAID_EXT_Q = 26'h1 << 16; + parameter logic [25:0] MISAID_EXT_R = 26'h1 << 17; + parameter logic [25:0] MISAID_EXT_S = 26'h1 << 18; + parameter logic [25:0] MISAID_EXT_T = 26'h1 << 19; + parameter logic [25:0] MISAID_EXT_U = 26'h1 << 20; + parameter logic [25:0] MISAID_EXT_V = 26'h1 << 21; + parameter logic [25:0] MISAID_EXT_W = 26'h1 << 22; + parameter logic [25:0] MISAID_EXT_X = 26'h1 << 23; + parameter logic [25:0] MISAID_EXT_Y = 26'h1 << 24; + parameter logic [25:0] MISAID_EXT_Z = 26'h1 << 25; - typedef struct packed { - misaid_base_t base; - logic [3:0] zero; - logic [25:0] extensions; - } misaid_t; + /* mstatus types */ - parameter MISAID_EXT_A = 26'h1 << 0; - parameter MISAID_EXT_B = 26'h1 << 1; - parameter MISAID_EXT_C = 26'h1 << 2; - parameter MISAID_EXT_D = 26'h1 << 3; - parameter MISAID_EXT_E = 26'h1 << 4; - parameter MISAID_EXT_F = 26'h1 << 5; - parameter MISAID_EXT_G = 26'h1 << 6; - parameter MISAID_EXT_H = 26'h1 << 7; - parameter MISAID_EXT_I = 26'h1 << 8; - parameter MISAID_EXT_J = 26'h1 << 9; - parameter MISAID_EXT_K = 26'h1 << 10; - parameter MISAID_EXT_L = 26'h1 << 11; - parameter MISAID_EXT_M = 26'h1 << 12; - parameter MISAID_EXT_N = 26'h1 << 13; - parameter MISAID_EXT_O = 26'h1 << 14; - parameter MISAID_EXT_P = 26'h1 << 15; - parameter MISAID_EXT_Q = 26'h1 << 16; - parameter MISAID_EXT_R = 26'h1 << 17; - parameter MISAID_EXT_S = 26'h1 << 18; - parameter MISAID_EXT_T = 26'h1 << 19; - parameter MISAID_EXT_U = 26'h1 << 20; - parameter MISAID_EXT_V = 26'h1 << 21; - parameter MISAID_EXT_W = 26'h1 << 22; - parameter MISAID_EXT_X = 26'h1 << 23; - parameter MISAID_EXT_Y = 26'h1 << 24; - parameter MISAID_EXT_Z = 26'h1 << 25; + typedef enum logic [1:0] { + FS_OFF = 2'h0, + FS_INITIAL = 2'h1, + FS_CLEAN = 2'h2, + FS_DIRTY = 2'h3 + } fs_t; - /* mstatus types */ + typedef enum logic [1:0] { + XS_ALL_OFF = 2'h0, + XS_NONE_DC = 2'h1, + XS_NONE_D = 2'h2, + XS_SOME_D = 2'h3 + } xs_t; - typedef enum logic [1:0] { - FS_OFF = 2'h0, - FS_INITIAL = 2'h1, - FS_CLEAN = 2'h2, - FS_DIRTY = 2'h3 - } fs_t; + typedef enum logic [1:0] { + U_LEVEL = 2'h0, + S_LEVEL = 2'h1, + RESERVED_LEVEL = 2'h2, + M_LEVEL = 2'h3 + } priv_level_t; - typedef enum logic [1:0] { - XS_ALL_OFF = 2'h0, - XS_NONE_DC = 2'h1, - XS_NONE_D = 2'h2, - XS_SOME_D = 2'h3 - } xs_t; + typedef enum logic [1:0] { + DIRECT = 2'h0, + VECTORED = 2'h1 + } vector_modes_t; - typedef enum logic [1:0] { - U_LEVEL = 2'h0, - S_LEVEL = 2'h1, - RESERVED_LEVEL = 2'h2, - M_LEVEL = 2'h3 - } priv_level_t; + typedef struct packed { + logic [29:0] base; + vector_modes_t mode; + } mtvec_t; - typedef enum logic [1:0] { - DIRECT = 2'h0, - VECTORED = 2'h1 - } vector_modes_t; + typedef struct packed { + logic sd; + logic [7:0] reserved_3; + logic tsr; + logic tw; + logic tvm; + logic mxr; + logic sum; + logic mprv; + xs_t xs; + fs_t fs; + priv_level_t mpp; + logic [1:0] reserved_2; + logic spp; + logic mpie; + logic reserved_1; + logic spie; + logic upie; + logic mie; + logic reserved_0; + logic sie; + logic uie; + } mstatus_t; - typedef struct packed { - logic [29:0] base; - vector_modes_t mode; - } mtvec_t; - - typedef struct packed { - logic sd; - logic [7:0] reserved_3; - logic tsr; - logic tw; - logic tvm; - logic mxr; - logic sum; - logic mprv; - xs_t xs; - fs_t fs; - priv_level_t mpp; - logic [1:0] reserved_2; - logic spp; - logic mpie; - logic reserved_1; - logic spie; - logic upie; - logic mie; - logic reserved_0; - logic sie; - logic uie; - } mstatus_t; + /* mip and mie types */ - /* mip and mie types */ + typedef struct packed { // total size for xlen (or mxlen) is 32 bits for our processor + logic [19:0] reserved_3; + logic meip; + logic reserved_2; + logic seip; + logic ueip; + logic mtip; + logic reserved_1; + logic stip; + logic utip; + logic msip; + logic reserved_0; + logic ssip; + logic usip; + } mip_t; - typedef struct packed { // total size for xlen (or mxlen) is 32 bits for our processor - logic [19:0] reserved_3; - logic meip; - logic reserved_2; - logic seip; - logic ueip; - logic mtip; - logic reserved_1; - logic stip; - logic utip; - logic msip; - logic reserved_0; - logic ssip; - logic usip; - } mip_t; + typedef struct packed { + logic [19:0] reserved_3; + logic meie; + logic reserved_2; + logic seie; + logic ueie; + logic mtie; + logic reserved_1; + logic stie; + logic utie; + logic msie; + logic reserved_0; + logic ssie; + logic usie; + } mie_t; - typedef struct packed { - logic [19:0] reserved_3; - logic meie; - logic reserved_2; - logic seie; - logic ueie; - logic mtie; - logic reserved_1; - logic stie; - logic utie; - logic msie; - logic reserved_0; - logic ssie; - logic usie; - } mie_t; + /* mcause register variables */ - /* mcause register variables */ + typedef struct packed { + logic interrupt; + logic [30:0] cause; + } mcause_t; - typedef struct packed { - logic interrupt; - logic [30:0] cause; - } mcause_t; + // ex_code_t should be cast from an + // instantiation of mcause_t + typedef enum logic [30:0] { + INSN_MAL = 31'd0, + INSN_ACCESS = 31'd1, + ILLEGAL_INSN = 31'd2, + BREAKPOINT = 31'd3, + L_ADDR_MAL = 31'd4, + L_FAULT = 31'd5, + S_ADDR_MAL = 31'd6, + S_FAULT = 31'd7, + ENV_CALL_U = 31'd8, + ENV_CALL_S = 31'd9, + RESERVED_0 = 31'd10, + ENV_CALL_M = 31'd11, + INSN_PAGE = 31'd12, + LOAD_PAGE = 31'd13, + RESERVED_1 = 31'd14, + STORE_PAGE = 31'd15, + RESERVED_2 = 31'd16 + } ex_code_t; - // ex_code_t should be cast from an - // instantiation of mcause_t - typedef enum logic [30:0] { - INSN_MAL = 31'd0, - INSN_ACCESS = 31'd1, - ILLEGAL_INSN = 31'd2, - BREAKPOINT = 31'd3, - L_ADDR_MAL = 31'd4, - L_FAULT = 31'd5, - S_ADDR_MAL = 31'd6, - S_FAULT = 31'd7, - ENV_CALL_U = 31'd8, - ENV_CALL_S = 31'd9, - RESERVED_0 = 31'd10, - ENV_CALL_M = 31'd11, - INSN_PAGE = 31'd12, - LOAD_PAGE = 31'd13, - RESERVED_1 = 31'd14, - STORE_PAGE = 31'd15, - RESERVED_2 = 31'd16 - } ex_code_t; + typedef enum logic [30:0] { + SOFT_INT_U = 31'd0, + SOFT_INT_S = 31'd1, + RESERVED_4 = 31'd2, + SOFT_INT_M = 31'd3, + TIMER_INT_U = 31'd4, + TIMER_INT_S = 31'd5, + RESERVED_5 = 31'd6, + TIMER_INT_M = 31'd7, + EXT_INT_U = 31'd8, + EXT_INT_S = 31'd9, + RESERVED_6 = 31'd10, + EXT_INT_M = 31'd11 + } int_code_t; - typedef enum logic [30:0] { - SOFT_INT_U = 31'd0, - SOFT_INT_S = 31'd1, - RESERVED_4 = 31'd2, - SOFT_INT_M = 31'd3, - TIMER_INT_U = 31'd4, - TIMER_INT_S = 31'd5, - RESERVED_5 = 31'd6, - TIMER_INT_M = 31'd7, - EXT_INT_U = 31'd8, - EXT_INT_S = 31'd9, - RESERVED_6 = 31'd10, - EXT_INT_M = 31'd11 - } int_code_t; + /* Simple registers */ - /* Simple registers */ + typedef logic [63:0] mcycle_t; + typedef logic [63:0] minstret_t; + typedef logic [31:0] mscratch_t; + typedef logic [31:0] mtval_t; + typedef logic [31:0] mvendorid_t; + typedef logic [31:0] marchid_t; + typedef logic [31:0] mimpid_t; + typedef logic [31:0] mhartid_t; + typedef logic [31:0] medeleg_t; + typedef logic [31:0] mideleg_t; + //typedef logic [31:0] mtvec_t; + typedef logic [31:0] mepc_t; + typedef logic [31:0] mtime_t; + typedef logic [31:0] mtimeh_t; + typedef logic [31:0] mtimecmp_t; - typedef logic [63:0] mcycle_t; - typedef logic [63:0] minstret_t; - typedef logic [31:0] mscratch_t; - typedef logic [31:0] mtval_t; - typedef logic [31:0] mvendorid_t; - typedef logic [31:0] marchid_t; - typedef logic [31:0] mimpid_t; - typedef logic [31:0] mhartid_t; - typedef logic [31:0] medeleg_t; - typedef logic [31:0] mideleg_t; - //typedef logic [31:0] mtvec_t; - typedef logic [31:0] mepc_t; - typedef logic [31:0] mtime_t; - typedef logic [31:0] mtimeh_t; - typedef logic [31:0] mtimecmp_t; + /* User simple registers */ + typedef logic [31:0] cycle_t; + typedef logic [31:0] time_t; + typedef logic [31:0] instret_t; - /* User simple registers */ - typedef logic [31:0] cycle_t; - typedef logic [31:0] time_t; - typedef logic [31:0] instret_t; + //Non Standard Extentions + typedef logic [31:0] mtohost_t; + typedef logic [31:0] mfromhost_t; - //Non Standard Extentions - typedef logic [31:0] mtohost_t; - typedef logic [31:0] mfromhost_t; - endpackage -`endif //MACHINE_MODE_TYPES_1_11_PKG_SV +`endif //MACHINE_MODE_TYPES_1_11_PKG_SV diff --git a/source_code/packages/machine_mode_types_1_12_pkg.sv b/source_code/packages/machine_mode_types_1_12_pkg.sv index 565544389..b10970898 100644 --- a/source_code/packages/machine_mode_types_1_12_pkg.sv +++ b/source_code/packages/machine_mode_types_1_12_pkg.sv @@ -178,32 +178,32 @@ package machine_mode_types_1_12_pkg; logic [25:0] extensions; } misa_t; - parameter MISA_EXT_A = 26'h1 << 0; - parameter MISA_EXT_B = 26'h1 << 1; - parameter MISA_EXT_C = 26'h1 << 2; - parameter MISA_EXT_D = 26'h1 << 3; - parameter MISA_EXT_E = 26'h1 << 4; - parameter MISA_EXT_F = 26'h1 << 5; - parameter MISA_EXT_G = 26'h1 << 6; - parameter MISA_EXT_H = 26'h1 << 7; - parameter MISA_EXT_I = 26'h1 << 8; - parameter MISA_EXT_J = 26'h1 << 9; - parameter MISA_EXT_K = 26'h1 << 10; - parameter MISA_EXT_L = 26'h1 << 11; - parameter MISA_EXT_M = 26'h1 << 12; - parameter MISA_EXT_N = 26'h1 << 13; - parameter MISA_EXT_O = 26'h1 << 14; - parameter MISA_EXT_P = 26'h1 << 15; - parameter MISA_EXT_Q = 26'h1 << 16; - parameter MISA_EXT_R = 26'h1 << 17; - parameter MISA_EXT_S = 26'h1 << 18; - parameter MISA_EXT_T = 26'h1 << 19; - parameter MISA_EXT_U = 26'h1 << 20; - parameter MISA_EXT_V = 26'h1 << 21; - parameter MISA_EXT_W = 26'h1 << 22; - parameter MISA_EXT_X = 26'h1 << 23; - parameter MISA_EXT_Y = 26'h1 << 24; - parameter MISA_EXT_Z = 26'h1 << 25; + localparam logic[25:0] MISA_EXT_A = 26'h1 << 0; + localparam logic[25:0] MISA_EXT_B = 26'h1 << 1; + localparam logic[25:0] MISA_EXT_C = 26'h1 << 2; + localparam logic[25:0] MISA_EXT_D = 26'h1 << 3; + localparam logic[25:0] MISA_EXT_E = 26'h1 << 4; + localparam logic[25:0] MISA_EXT_F = 26'h1 << 5; + localparam logic[25:0] MISA_EXT_G = 26'h1 << 6; + localparam logic[25:0] MISA_EXT_H = 26'h1 << 7; + localparam logic[25:0] MISA_EXT_I = 26'h1 << 8; + localparam logic[25:0] MISA_EXT_J = 26'h1 << 9; + localparam logic[25:0] MISA_EXT_K = 26'h1 << 10; + localparam logic[25:0] MISA_EXT_L = 26'h1 << 11; + localparam logic[25:0] MISA_EXT_M = 26'h1 << 12; + localparam logic[25:0] MISA_EXT_N = 26'h1 << 13; + localparam logic[25:0] MISA_EXT_O = 26'h1 << 14; + localparam logic[25:0] MISA_EXT_P = 26'h1 << 15; + localparam logic[25:0] MISA_EXT_Q = 26'h1 << 16; + localparam logic[25:0] MISA_EXT_R = 26'h1 << 17; + localparam logic[25:0] MISA_EXT_S = 26'h1 << 18; + localparam logic[25:0] MISA_EXT_T = 26'h1 << 19; + localparam logic[25:0] MISA_EXT_U = 26'h1 << 20; + localparam logic[25:0] MISA_EXT_V = 26'h1 << 21; + localparam logic[25:0] MISA_EXT_W = 26'h1 << 22; + localparam logic[25:0] MISA_EXT_X = 26'h1 << 23; + localparam logic[25:0] MISA_EXT_Y = 26'h1 << 24; + localparam logic[25:0] MISA_EXT_Z = 26'h1 << 25; /* mstatus types */ diff --git a/source_code/packages/machine_mode_types_1_7_pkg.sv b/source_code/packages/machine_mode_types_1_7_pkg.sv index 52952a72e..2c47b7e6d 100644 --- a/source_code/packages/machine_mode_types_1_7_pkg.sv +++ b/source_code/packages/machine_mode_types_1_7_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,225 +27,225 @@ package machine_mode_types_1_7_pkg; - typedef enum logic [11:0] { - /* Machine Mode Addresses */ - MCPUID_ADDR = 12'hF00, - MIMPID_ADDR = 12'hF01, - MHARTID_ADDR = 12'hF10, - - MSTATUS_ADDR = 12'h300, - MTVEC_ADDR = 12'h301, - MTDELEG_ADDR = 12'h302, - MIE_ADDR = 12'h304, - - MSCRATCH_ADDR = 12'h340, - MEPC_ADDR = 12'h341, - MCAUSE_ADDR = 12'h342, - MBADADDR_ADDR = 12'h343, - MIP_ADDR = 12'h344, - - MBASE_ADDR = 12'h380, - MBOUND_ADDR = 12'h381, - MIBASE_ADDR = 12'h382, - MIBOUND_ADDR = 12'h383, - MDBASE_ADDR = 12'h384, - MDBOUND_ADDR = 12'h385, - - HTIMEW_ADDR = 12'hB01, - HTIMEHW_ADDR = 12'hB81, - - MTIMECMP_ADDR = 12'h321, - MTIME_ADDR = 12'h701, - MTIMEH_ADDR = 12'h741, - - MTOHOST_ADDR = 12'h780, - MFROMHOST_ADDR = 12'h781, - /* User Mode Addresses */ - CYCLE_ADDR = 12'hC00, - TIME_ADDR = 12'hC01, - INSTRET_ADDR = 12'hC02, - CYCLEH_ADDR = 12'hC80, - TIMEH_ADDR = 12'hC81, - INSTRETH_ADDR = 12'hC82 - } csr_addr_t; - - - /* Priv Levels */ - typedef enum logic [1:0] { - U_MODE = 2'b00, - S_MODE = 2'b01, - H_MODE = 2'b10, - M_MODE = 2'b11 - } prv_lvl_t; - - /* Machine Mode Register Types */ - - /* mcpuid types */ - - typedef enum logic [1:0] { - BASE_RV32 = 2'h1, - BASE_RV64 = 2'h2, - BASE_RV128 = 2'h3 - } mcpuid_base_t; - - typedef struct packed { - mcpuid_base_t base; - logic [3:0] zero; - logic [25:0] extensions; - } mcpuid_t; - - parameter MCPUID_EXT_A = 26'h1 << 0; - parameter MCPUID_EXT_B = 26'h1 << 1; - parameter MCPUID_EXT_C = 26'h1 << 2; - parameter MCPUID_EXT_D = 26'h1 << 3; - parameter MCPUID_EXT_E = 26'h1 << 4; - parameter MCPUID_EXT_F = 26'h1 << 5; - parameter MCPUID_EXT_G = 26'h1 << 6; - parameter MCPUID_EXT_H = 26'h1 << 7; - parameter MCPUID_EXT_I = 26'h1 << 8; - parameter MCPUID_EXT_J = 26'h1 << 9; - parameter MCPUID_EXT_K = 26'h1 << 10; - parameter MCPUID_EXT_L = 26'h1 << 11; - parameter MCPUID_EXT_M = 26'h1 << 12; - parameter MCPUID_EXT_N = 26'h1 << 13; - parameter MCPUID_EXT_O = 26'h1 << 14; - parameter MCPUID_EXT_P = 26'h1 << 15; - parameter MCPUID_EXT_Q = 26'h1 << 16; - parameter MCPUID_EXT_R = 26'h1 << 17; - parameter MCPUID_EXT_S = 26'h1 << 18; - parameter MCPUID_EXT_T = 26'h1 << 19; - parameter MCPUID_EXT_U = 26'h1 << 20; - parameter MCPUID_EXT_V = 26'h1 << 21; - parameter MCPUID_EXT_W = 26'h1 << 22; - parameter MCPUID_EXT_X = 26'h1 << 23; - parameter MCPUID_EXT_Y = 26'h1 << 24; - parameter MCPUID_EXT_Z = 26'h1 << 25; - parameter MTVEC_MEMORY_ADDR = 32'h1c0; - - /* mstatus types */ - - typedef enum logic [4:0] { - VM_MBARE = 5'h0, - VM_MBB = 5'h1, - VM_MBBID = 5'h2, - VM_SV32 = 5'h8, - VM_SV39 = 5'h9, - VM_SV48 = 5'ha, - VM_SV57 = 5'hb, - VM_SV64 = 5'hc - } vm_t; - - typedef enum logic [1:0] { - FS_OFF = 2'h0, - FS_INITIAL = 2'h1, - FS_CLEAN = 2'h2, - FS_DIRTY = 2'h3 - } fs_t; - - typedef enum logic [1:0] { - XS_ALL_OFF = 2'h0, - XS_NONE_DC = 2'h1, - XS_NONE_D = 2'h2, - XS_SOME_D = 2'h3 - } xs_t; - - typedef struct packed { - logic sd; - logic [8:0] zero; - vm_t vm; - logic mprv; - xs_t xs; - fs_t fs; - prv_lvl_t prv3; - logic ie3; - prv_lvl_t prv2; - logic ie2; - prv_lvl_t prv1; - logic ie1; - prv_lvl_t prv; - logic ie; - } mstatus_t; - - /* mip and mie types */ - - typedef struct packed { - logic [23:0] zero_2; - logic mtip; - logic htip; - logic stip; - logic zero_1; - logic msip; - logic hsip; - logic ssip; - logic zero_0; - } mip_t; - - typedef struct packed { - logic [23:0] zero_2; - logic mtie; - logic htie; - logic stie; - logic zero_1; - logic msie; - logic hsie; - logic ssie; - logic zero_0; - } mie_t; - - /* mcause register variables */ - - typedef struct packed { - logic interrupt; - logic [30:0] cause; - } mcause_t; - - // ex_code_t should be cast from an - // instantiation of mcause_t - typedef enum logic [30:0] { - INSN_MAL = 31'h0, - INSN_FAULT = 31'h1, - ILLEGAL_INSN = 31'h2, - BREAKPOINT = 31'h3, - L_ADDR_MAL = 31'h4, - L_FAULT = 31'h5, - S_ADDR_MAL = 31'h6, - S_FAULT = 31'h7, - ENV_CALL_U = 31'h8, - ENV_CALL_S = 31'h9, - ENV_CALL_H = 31'ha, - ENV_CALL_M = 31'hb - } ex_code_t; - - typedef enum logic [30:0] { - SOFT_INT = 31'h0, - TIMER_INT = 31'h1, - EXT_INT = 31'hb //NON-STANDARD in priv-1.7 - } int_code_t; - - /* Simple registers */ - - typedef logic [63:0] mcycle_t; - typedef logic [63:0] minstret_t; - typedef logic [31:0] mscratch_t; - typedef logic [31:0] mbadaddr_t; - typedef logic [31:0] mimpid_t; - typedef logic [31:0] mhartid_t; - typedef logic [31:0] mtdeleg_t; - typedef logic [31:0] mtvec_t; - typedef logic [31:0] mepc_t; - typedef logic [31:0] mtime_t; - typedef logic [31:0] mtimeh_t; - typedef logic [31:0] mtimecmp_t; - - /* User simple registers */ - typedef logic [31:0] cycle_t; - typedef logic [31:0] time_t; - typedef logic [31:0] instret_t; - - //Non Standard Extentions - typedef logic [31:0] mtohost_t; - typedef logic [31:0] mfromhost_t; - + typedef enum logic [11:0] { + /* Machine Mode Addresses */ + MCPUID_ADDR = 12'hF00, + MIMPID_ADDR = 12'hF01, + MHARTID_ADDR = 12'hF10, + + MSTATUS_ADDR = 12'h300, + MTVEC_ADDR = 12'h301, + MTDELEG_ADDR = 12'h302, + MIE_ADDR = 12'h304, + + MSCRATCH_ADDR = 12'h340, + MEPC_ADDR = 12'h341, + MCAUSE_ADDR = 12'h342, + MBADADDR_ADDR = 12'h343, + MIP_ADDR = 12'h344, + + MBASE_ADDR = 12'h380, + MBOUND_ADDR = 12'h381, + MIBASE_ADDR = 12'h382, + MIBOUND_ADDR = 12'h383, + MDBASE_ADDR = 12'h384, + MDBOUND_ADDR = 12'h385, + + HTIMEW_ADDR = 12'hB01, + HTIMEHW_ADDR = 12'hB81, + + MTIMECMP_ADDR = 12'h321, + MTIME_ADDR = 12'h701, + MTIMEH_ADDR = 12'h741, + + MTOHOST_ADDR = 12'h780, + MFROMHOST_ADDR = 12'h781, + /* User Mode Addresses */ + CYCLE_ADDR = 12'hC00, + TIME_ADDR = 12'hC01, + INSTRET_ADDR = 12'hC02, + CYCLEH_ADDR = 12'hC80, + TIMEH_ADDR = 12'hC81, + INSTRETH_ADDR = 12'hC82 + } csr_addr_t; + + + /* Priv Levels */ + typedef enum logic [1:0] { + U_MODE = 2'b00, + S_MODE = 2'b01, + H_MODE = 2'b10, + M_MODE = 2'b11 + } prv_lvl_t; + + /* Machine Mode Register Types */ + + /* mcpuid types */ + + typedef enum logic [1:0] { + BASE_RV32 = 2'h1, + BASE_RV64 = 2'h2, + BASE_RV128 = 2'h3 + } mcpuid_base_t; + + typedef struct packed { + mcpuid_base_t base; + logic [3:0] zero; + logic [25:0] extensions; + } mcpuid_t; + + parameter MCPUID_EXT_A = 26'h1 << 0; + parameter MCPUID_EXT_B = 26'h1 << 1; + parameter MCPUID_EXT_C = 26'h1 << 2; + parameter MCPUID_EXT_D = 26'h1 << 3; + parameter MCPUID_EXT_E = 26'h1 << 4; + parameter MCPUID_EXT_F = 26'h1 << 5; + parameter MCPUID_EXT_G = 26'h1 << 6; + parameter MCPUID_EXT_H = 26'h1 << 7; + parameter MCPUID_EXT_I = 26'h1 << 8; + parameter MCPUID_EXT_J = 26'h1 << 9; + parameter MCPUID_EXT_K = 26'h1 << 10; + parameter MCPUID_EXT_L = 26'h1 << 11; + parameter MCPUID_EXT_M = 26'h1 << 12; + parameter MCPUID_EXT_N = 26'h1 << 13; + parameter MCPUID_EXT_O = 26'h1 << 14; + parameter MCPUID_EXT_P = 26'h1 << 15; + parameter MCPUID_EXT_Q = 26'h1 << 16; + parameter MCPUID_EXT_R = 26'h1 << 17; + parameter MCPUID_EXT_S = 26'h1 << 18; + parameter MCPUID_EXT_T = 26'h1 << 19; + parameter MCPUID_EXT_U = 26'h1 << 20; + parameter MCPUID_EXT_V = 26'h1 << 21; + parameter MCPUID_EXT_W = 26'h1 << 22; + parameter MCPUID_EXT_X = 26'h1 << 23; + parameter MCPUID_EXT_Y = 26'h1 << 24; + parameter MCPUID_EXT_Z = 26'h1 << 25; + parameter MTVEC_MEMORY_ADDR = 32'h1c0; + + /* mstatus types */ + + typedef enum logic [4:0] { + VM_MBARE = 5'h0, + VM_MBB = 5'h1, + VM_MBBID = 5'h2, + VM_SV32 = 5'h8, + VM_SV39 = 5'h9, + VM_SV48 = 5'ha, + VM_SV57 = 5'hb, + VM_SV64 = 5'hc + } vm_t; + + typedef enum logic [1:0] { + FS_OFF = 2'h0, + FS_INITIAL = 2'h1, + FS_CLEAN = 2'h2, + FS_DIRTY = 2'h3 + } fs_t; + + typedef enum logic [1:0] { + XS_ALL_OFF = 2'h0, + XS_NONE_DC = 2'h1, + XS_NONE_D = 2'h2, + XS_SOME_D = 2'h3 + } xs_t; + + typedef struct packed { + logic sd; + logic [8:0] zero; + vm_t vm; + logic mprv; + xs_t xs; + fs_t fs; + prv_lvl_t prv3; + logic ie3; + prv_lvl_t prv2; + logic ie2; + prv_lvl_t prv1; + logic ie1; + prv_lvl_t prv; + logic ie; + } mstatus_t; + + /* mip and mie types */ + + typedef struct packed { + logic [23:0] zero_2; + logic mtip; + logic htip; + logic stip; + logic zero_1; + logic msip; + logic hsip; + logic ssip; + logic zero_0; + } mip_t; + + typedef struct packed { + logic [23:0] zero_2; + logic mtie; + logic htie; + logic stie; + logic zero_1; + logic msie; + logic hsie; + logic ssie; + logic zero_0; + } mie_t; + + /* mcause register variables */ + + typedef struct packed { + logic interrupt; + logic [30:0] cause; + } mcause_t; + + // ex_code_t should be cast from an + // instantiation of mcause_t + typedef enum logic [30:0] { + INSN_MAL = 31'h0, + INSN_FAULT = 31'h1, + ILLEGAL_INSN = 31'h2, + BREAKPOINT = 31'h3, + L_ADDR_MAL = 31'h4, + L_FAULT = 31'h5, + S_ADDR_MAL = 31'h6, + S_FAULT = 31'h7, + ENV_CALL_U = 31'h8, + ENV_CALL_S = 31'h9, + ENV_CALL_H = 31'ha, + ENV_CALL_M = 31'hb + } ex_code_t; + + typedef enum logic [30:0] { + SOFT_INT = 31'h0, + TIMER_INT = 31'h1, + EXT_INT = 31'hb //NON-STANDARD in priv-1.7 + } int_code_t; + + /* Simple registers */ + + typedef logic [63:0] mcycle_t; + typedef logic [63:0] minstret_t; + typedef logic [31:0] mscratch_t; + typedef logic [31:0] mbadaddr_t; + typedef logic [31:0] mimpid_t; + typedef logic [31:0] mhartid_t; + typedef logic [31:0] mtdeleg_t; + typedef logic [31:0] mtvec_t; + typedef logic [31:0] mepc_t; + typedef logic [31:0] mtime_t; + typedef logic [31:0] mtimeh_t; + typedef logic [31:0] mtimecmp_t; + + /* User simple registers */ + typedef logic [31:0] cycle_t; + typedef logic [31:0] time_t; + typedef logic [31:0] instret_t; + + //Non Standard Extentions + typedef logic [31:0] mtohost_t; + typedef logic [31:0] mfromhost_t; + endpackage -`endif //MACHINE_MODE_TYPES_1_7_PKG_SV +`endif //MACHINE_MODE_TYPES_1_7_PKG_SV diff --git a/source_code/packages/machine_mode_types_pkg.sv b/source_code/packages/machine_mode_types_pkg.sv index 729617c32..7ca1a78b9 100644 --- a/source_code/packages/machine_mode_types_pkg.sv +++ b/source_code/packages/machine_mode_types_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,230 +29,230 @@ package machine_mode_types_pkg; - typedef enum logic [11:0] { - /* Machine Mode Addresses */ - MVENDORID_ADDR = 12'hF11, - MARCHID_ADDR = 12'hF12, - MIMPID_ADDR = 12'hF13, - MHARTID_ADDR = 12'hF14, - - MSTATUS_ADDR = 12'h300, - MISA_ADDR = 12'h301, - MEDELEG_ADDR = 12'h302, - MIDELEG_ADDR = 12'h303, - MIE_ADDR = 12'h304, - MTVEC_ADDR = 12'h305, - //TODO: MCOUNTEREN AND ASSOCIATED REGS/CONTGROL (e.g. cycle/time) - MCOUNTEREN_ADDR = 12'h306, - - MSCRATCH_ADDR = 12'h340, - MEPC_ADDR = 12'h341, - MCAUSE_ADDR = 12'h342, - MTVAL_ADDR = 12'h343, - MIP_ADDR = 12'h344, - - // TODO: MAY BE ABLE TO REMOVE BELOW - MBASE_ADDR = 12'h380, - MBOUND_ADDR = 12'h381, - MIBASE_ADDR = 12'h382, - MIBOUND_ADDR = 12'h383, - MDBASE_ADDR = 12'h384, - MDBOUND_ADDR = 12'h385, - // TODO: MAY BE ABLE TO REMOVE ABOVE - // TODO: BELOW MUST BE REMOVED - MTOHOST_ADDR = 12'h780, - MFROMHOST_ADDR = 12'h781, - HTIMEW_ADDR = 12'hB01, - HTIMEHW_ADDR = 12'hB81, - MTIMECMP_ADDR = 12'h321, - MTIME_ADDR = 12'h701, - MTIMEH_ADDR = 12'h741, - // TODO: ABOVE MUST BE REMOVED - - MCYCLE_ADDR = 12'hB00, - MINSTRET_ADDR = 12'hB02, - MCYCLEH_ADDR = 12'hB80, - MINSTRETH_ADDR = 12'hB82 - } csr_addr_t; - - /* Priv Levels */ - typedef enum logic [1:0] { - U_MODE = 2'b00, - S_MODE = 2'b01, - H_MODE = 2'b10, - M_MODE = 2'b11 - } prv_lvl_t; - - /* Machine Mode Register Types */ - - /* misaid types */ - - typedef enum logic [1:0] { - BASE_RV32 = 2'h1, - BASE_RV64 = 2'h2, - BASE_RV128 = 2'h3 - } misaid_base_t; - - typedef struct packed { - misaid_base_t base; - logic [3:0] zero; - logic [25:0] extensions; - } misaid_t; - - parameter MISAID_EXT_A = 26'h1 << 0; - parameter MISAID_EXT_B = 26'h1 << 1; - parameter MISAID_EXT_C = 26'h1 << 2; - parameter MISAID_EXT_D = 26'h1 << 3; - parameter MISAID_EXT_E = 26'h1 << 4; - parameter MISAID_EXT_F = 26'h1 << 5; - parameter MISAID_EXT_G = 26'h1 << 6; - parameter MISAID_EXT_H = 26'h1 << 7; - parameter MISAID_EXT_I = 26'h1 << 8; - parameter MISAID_EXT_J = 26'h1 << 9; - parameter MISAID_EXT_K = 26'h1 << 10; - parameter MISAID_EXT_L = 26'h1 << 11; - parameter MISAID_EXT_M = 26'h1 << 12; - parameter MISAID_EXT_N = 26'h1 << 13; - parameter MISAID_EXT_O = 26'h1 << 14; - parameter MISAID_EXT_P = 26'h1 << 15; - parameter MISAID_EXT_Q = 26'h1 << 16; - parameter MISAID_EXT_R = 26'h1 << 17; - parameter MISAID_EXT_S = 26'h1 << 18; - parameter MISAID_EXT_T = 26'h1 << 19; - parameter MISAID_EXT_U = 26'h1 << 20; - parameter MISAID_EXT_V = 26'h1 << 21; - parameter MISAID_EXT_W = 26'h1 << 22; - parameter MISAID_EXT_X = 26'h1 << 23; - parameter MISAID_EXT_Y = 26'h1 << 24; - parameter MISAID_EXT_Z = 26'h1 << 25; - - /* mstatus types */ - - typedef enum logic [4:0] { - VM_MBARE = 5'h0, - VM_MBB = 5'h1, - VM_MBBID = 5'h2, - VM_SV32 = 5'h8, - VM_SV39 = 5'h9, - VM_SV48 = 5'ha, - VM_SV57 = 5'hb, - VM_SV64 = 5'hc - } vm_t; - - typedef enum logic [1:0] { - FS_OFF = 2'h0, - FS_INITIAL = 2'h1, - FS_CLEAN = 2'h2, - FS_DIRTY = 2'h3 - } fs_t; - - typedef enum logic [1:0] { - XS_ALL_OFF = 2'h0, - XS_NONE_DC = 2'h1, - XS_NONE_D = 2'h2, - XS_SOME_D = 2'h3 - } xs_t; - - typedef struct packed { - logic sd; - logic [8:0] zero; - vm_t vm; - logic mprv; - xs_t xs; - fs_t fs; - prv_lvl_t prv3; - logic ie3; - prv_lvl_t prv2; - logic ie2; - prv_lvl_t prv1; - logic ie1; - prv_lvl_t prv; - logic ie; - } mstatus_t; - - /* mip and mie types */ - - typedef struct packed { - logic [23:0] zero_2; - logic mtip; - logic htip; - logic stip; - logic zero_1; - logic msip; - logic hsip; - logic ssip; - logic zero_0; - } mip_t; - - typedef struct packed { - logic [23:0] zero_2; - logic mtie; - logic htie; - logic stie; - logic zero_1; - logic msie; - logic hsie; - logic ssie; - logic zero_0; - } mie_t; - - /* mcause register variables */ - - typedef struct packed { - logic interrupt; - logic [30:0] cause; - } mcause_t; - - // ex_code_t should be cast from an - // instantiation of mcause_t - typedef enum logic [30:0] { - INSN_MAL = 31'h0, - INSN_FAULT = 31'h1, - ILLEGAL_INSN = 31'h2, - BREAKPOINT = 31'h3, - L_ADDR_MAL = 31'h4, - L_FAULT = 31'h5, - S_ADDR_MAL = 31'h6, - S_FAULT = 31'h7, - ENV_CALL_U = 31'h8, - ENV_CALL_S = 31'h9, - ENV_CALL_H = 31'ha, - ENV_CALL_M = 31'hb - } ex_code_t; - - typedef enum logic [30:0] { - SOFT_INT = 31'h0, - TIMER_INT = 31'h1, - EXT_INT = 31'hb //NON-STANDARD in priv-1.7 - } int_code_t; - - /* Simple registers */ - - typedef logic [63:0] mcycle_t; - typedef logic [63:0] minstret_t; - typedef logic [31:0] mscratch_t; - typedef logic [31:0] mtval_t; - typedef logic [31:0] mvendorid_t; - typedef logic [31:0] marchid_t; - typedef logic [31:0] mimpid_t; - typedef logic [31:0] mhartid_t; - typedef logic [31:0] medeleg_t; - typedef logic [31:0] mideleg_t; - typedef logic [31:0] mtvec_t; - typedef logic [31:0] mepc_t; - typedef logic [31:0] mtime_t; - typedef logic [31:0] mtimeh_t; - typedef logic [31:0] mtimecmp_t; - - /* User simple registers */ - typedef logic [31:0] cycle_t; - typedef logic [31:0] time_t; - typedef logic [31:0] instret_t; - - //Non Standard Extentions - typedef logic [31:0] mtohost_t; - typedef logic [31:0] mfromhost_t; - + typedef enum logic [11:0] { + /* Machine Mode Addresses */ + MVENDORID_ADDR = 12'hF11, + MARCHID_ADDR = 12'hF12, + MIMPID_ADDR = 12'hF13, + MHARTID_ADDR = 12'hF14, + + MSTATUS_ADDR = 12'h300, + MISA_ADDR = 12'h301, + MEDELEG_ADDR = 12'h302, + MIDELEG_ADDR = 12'h303, + MIE_ADDR = 12'h304, + MTVEC_ADDR = 12'h305, + //TODO: MCOUNTEREN AND ASSOCIATED REGS/CONTGROL (e.g. cycle/time) + MCOUNTEREN_ADDR = 12'h306, + + MSCRATCH_ADDR = 12'h340, + MEPC_ADDR = 12'h341, + MCAUSE_ADDR = 12'h342, + MTVAL_ADDR = 12'h343, + MIP_ADDR = 12'h344, + + // TODO: MAY BE ABLE TO REMOVE BELOW + MBASE_ADDR = 12'h380, + MBOUND_ADDR = 12'h381, + MIBASE_ADDR = 12'h382, + MIBOUND_ADDR = 12'h383, + MDBASE_ADDR = 12'h384, + MDBOUND_ADDR = 12'h385, + // TODO: MAY BE ABLE TO REMOVE ABOVE + // TODO: BELOW MUST BE REMOVED + MTOHOST_ADDR = 12'h780, + MFROMHOST_ADDR = 12'h781, + HTIMEW_ADDR = 12'hB01, + HTIMEHW_ADDR = 12'hB81, + MTIMECMP_ADDR = 12'h321, + MTIME_ADDR = 12'h701, + MTIMEH_ADDR = 12'h741, + // TODO: ABOVE MUST BE REMOVED + + MCYCLE_ADDR = 12'hB00, + MINSTRET_ADDR = 12'hB02, + MCYCLEH_ADDR = 12'hB80, + MINSTRETH_ADDR = 12'hB82 + } csr_addr_t; + + /* Priv Levels */ + typedef enum logic [1:0] { + U_MODE = 2'b00, + S_MODE = 2'b01, + H_MODE = 2'b10, + M_MODE = 2'b11 + } prv_lvl_t; + + /* Machine Mode Register Types */ + + /* misaid types */ + + typedef enum logic [1:0] { + BASE_RV32 = 2'h1, + BASE_RV64 = 2'h2, + BASE_RV128 = 2'h3 + } misaid_base_t; + + typedef struct packed { + misaid_base_t base; + logic [3:0] zero; + logic [25:0] extensions; + } misaid_t; + + parameter MISAID_EXT_A = 26'h1 << 0; + parameter MISAID_EXT_B = 26'h1 << 1; + parameter MISAID_EXT_C = 26'h1 << 2; + parameter MISAID_EXT_D = 26'h1 << 3; + parameter MISAID_EXT_E = 26'h1 << 4; + parameter MISAID_EXT_F = 26'h1 << 5; + parameter MISAID_EXT_G = 26'h1 << 6; + parameter MISAID_EXT_H = 26'h1 << 7; + parameter MISAID_EXT_I = 26'h1 << 8; + parameter MISAID_EXT_J = 26'h1 << 9; + parameter MISAID_EXT_K = 26'h1 << 10; + parameter MISAID_EXT_L = 26'h1 << 11; + parameter MISAID_EXT_M = 26'h1 << 12; + parameter MISAID_EXT_N = 26'h1 << 13; + parameter MISAID_EXT_O = 26'h1 << 14; + parameter MISAID_EXT_P = 26'h1 << 15; + parameter MISAID_EXT_Q = 26'h1 << 16; + parameter MISAID_EXT_R = 26'h1 << 17; + parameter MISAID_EXT_S = 26'h1 << 18; + parameter MISAID_EXT_T = 26'h1 << 19; + parameter MISAID_EXT_U = 26'h1 << 20; + parameter MISAID_EXT_V = 26'h1 << 21; + parameter MISAID_EXT_W = 26'h1 << 22; + parameter MISAID_EXT_X = 26'h1 << 23; + parameter MISAID_EXT_Y = 26'h1 << 24; + parameter MISAID_EXT_Z = 26'h1 << 25; + + /* mstatus types */ + + typedef enum logic [4:0] { + VM_MBARE = 5'h0, + VM_MBB = 5'h1, + VM_MBBID = 5'h2, + VM_SV32 = 5'h8, + VM_SV39 = 5'h9, + VM_SV48 = 5'ha, + VM_SV57 = 5'hb, + VM_SV64 = 5'hc + } vm_t; + + typedef enum logic [1:0] { + FS_OFF = 2'h0, + FS_INITIAL = 2'h1, + FS_CLEAN = 2'h2, + FS_DIRTY = 2'h3 + } fs_t; + + typedef enum logic [1:0] { + XS_ALL_OFF = 2'h0, + XS_NONE_DC = 2'h1, + XS_NONE_D = 2'h2, + XS_SOME_D = 2'h3 + } xs_t; + + typedef struct packed { + logic sd; + logic [8:0] zero; + vm_t vm; + logic mprv; + xs_t xs; + fs_t fs; + prv_lvl_t prv3; + logic ie3; + prv_lvl_t prv2; + logic ie2; + prv_lvl_t prv1; + logic ie1; + prv_lvl_t prv; + logic ie; + } mstatus_t; + + /* mip and mie types */ + + typedef struct packed { + logic [23:0] zero_2; + logic mtip; + logic htip; + logic stip; + logic zero_1; + logic msip; + logic hsip; + logic ssip; + logic zero_0; + } mip_t; + + typedef struct packed { + logic [23:0] zero_2; + logic mtie; + logic htie; + logic stie; + logic zero_1; + logic msie; + logic hsie; + logic ssie; + logic zero_0; + } mie_t; + + /* mcause register variables */ + + typedef struct packed { + logic interrupt; + logic [30:0] cause; + } mcause_t; + + // ex_code_t should be cast from an + // instantiation of mcause_t + typedef enum logic [30:0] { + INSN_MAL = 31'h0, + INSN_FAULT = 31'h1, + ILLEGAL_INSN = 31'h2, + BREAKPOINT = 31'h3, + L_ADDR_MAL = 31'h4, + L_FAULT = 31'h5, + S_ADDR_MAL = 31'h6, + S_FAULT = 31'h7, + ENV_CALL_U = 31'h8, + ENV_CALL_S = 31'h9, + ENV_CALL_H = 31'ha, + ENV_CALL_M = 31'hb + } ex_code_t; + + typedef enum logic [30:0] { + SOFT_INT = 31'h0, + TIMER_INT = 31'h1, + EXT_INT = 31'hb //NON-STANDARD in priv-1.7 + } int_code_t; + + /* Simple registers */ + + typedef logic [63:0] mcycle_t; + typedef logic [63:0] minstret_t; + typedef logic [31:0] mscratch_t; + typedef logic [31:0] mtval_t; + typedef logic [31:0] mvendorid_t; + typedef logic [31:0] marchid_t; + typedef logic [31:0] mimpid_t; + typedef logic [31:0] mhartid_t; + typedef logic [31:0] medeleg_t; + typedef logic [31:0] mideleg_t; + typedef logic [31:0] mtvec_t; + typedef logic [31:0] mepc_t; + typedef logic [31:0] mtime_t; + typedef logic [31:0] mtimeh_t; + typedef logic [31:0] mtimecmp_t; + + /* User simple registers */ + typedef logic [31:0] cycle_t; + typedef logic [31:0] time_t; + typedef logic [31:0] instret_t; + + //Non Standard Extentions + typedef logic [31:0] mtohost_t; + typedef logic [31:0] mfromhost_t; + endpackage -`endif //MACHINE_MODE_TYPES_PKG_SV +`endif //MACHINE_MODE_TYPES_PKG_SV diff --git a/source_code/packages/risc_mgmt/crc32_pkg.sv b/source_code/packages/risc_mgmt/crc32_pkg.sv index b6d34c157..e18f3ecef 100644 --- a/source_code/packages/risc_mgmt/crc32_pkg.sv +++ b/source_code/packages/risc_mgmt/crc32_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 04/06/2017 -* Description: Package for crc32 extension +* Description: Package for crc32 extension */ `ifndef CRC32_PKG_SV @@ -27,20 +27,18 @@ package crc32_pkg; - // Interface between the decode and execute stage - // This must be named "decode_execute_t" - typedef struct packed { - logic reset; - logic new_byte; - } decode_execute_t; + // Interface between the decode and execute stage + // This must be named "decode_execute_t" + typedef struct packed { + logic reset; + logic new_byte; + } decode_execute_t; - // Interface between the execute and memory stage - // This must be named "execute_memory_t" - typedef struct packed { - logic signal; - } execute_memory_t; + // Interface between the execute and memory stage + // This must be named "execute_memory_t" + typedef struct packed {logic signal;} execute_memory_t; endpackage -`endif //CRC32_PKG_SV +`endif //CRC32_PKG_SV diff --git a/source_code/packages/risc_mgmt/rv32m_pkg.sv b/source_code/packages/risc_mgmt/rv32m_pkg.sv index 7b2b40eaa..d55a6c0a1 100644 --- a/source_code/packages/risc_mgmt/rv32m_pkg.sv +++ b/source_code/packages/risc_mgmt/rv32m_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: Types for the RV32M standard extension +* Description: Types for the RV32M standard extension */ `ifndef RV32M_PKG_SV @@ -27,39 +27,37 @@ package rv32m_pkg; - localparam RV32M_OPCODE = 7'b0110011; - localparam RV32M_OPCODE_MINOR = 7'b0000001; - - typedef struct packed { - logic [6:0] opcode_minor; - logic [4:0] rs2; - logic [4:0] rs1; - logic [2:0] funct; - logic [4:0] rd; - logic [6:0] opcode_major; - } rv32m_insn_t; - - // Interface between the decode and execute stage - // This must be named "decode_execute_t" - typedef struct packed { - logic mul; - logic div; - logic rem; - logic usign_usign; - logic sign_sign; - logic sign_usign; - logic lower_word; - logic start; - } decode_execute_t; + localparam logic [6:0] RV32M_OPCODE = 7'b0110011; + localparam logic [6:0] RV32M_OPCODE_MINOR = 7'b0000001; + + typedef struct packed { + logic [6:0] opcode_minor; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct; + logic [4:0] rd; + logic [6:0] opcode_major; + } rv32m_insn_t; + + // Interface between the decode and execute stage + // This must be named "decode_execute_t" + typedef struct packed { + logic mul; + logic div; + logic rem; + logic usign_usign; + logic sign_sign; + logic sign_usign; + logic lower_word; + logic start; + } decode_execute_t; + + // Interface between the execute and memory stage + // This must be named "execute_memory_t" + typedef struct packed {logic signal;} execute_memory_t; - // Interface between the execute and memory stage - // This must be named "execute_memory_t" - typedef struct packed { - logic signal; - } execute_memory_t; - endpackage -`endif //RV32M_PKG_SV +`endif //RV32M_PKG_SV diff --git a/source_code/packages/risc_mgmt/template_pkg.sv b/source_code/packages/risc_mgmt/template_pkg.sv index 14cc8842c..d47d70afb 100644 --- a/source_code/packages/risc_mgmt/template_pkg.sv +++ b/source_code/packages/risc_mgmt/template_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,7 +18,7 @@ * * Created by: * Email: -* Date Created: +* Date Created: * Description: Template for a package containg the needed types for * a RISC-MGMT extension */ @@ -28,19 +28,15 @@ package template_pkg; - // Interface between the decode and execute stage - // This must be named "decode_execute_t" - typedef struct packed { - logic signal; - } decode_execute_t; + // Interface between the decode and execute stage + // This must be named "decode_execute_t" + typedef struct packed {logic signal;} decode_execute_t; - // Interface between the execute and memory stage - // This must be named "execute_memory_t" - typedef struct packed { - logic signal; - } execute_memory_t; + // Interface between the execute and memory stage + // This must be named "execute_memory_t" + typedef struct packed {logic signal;} execute_memory_t; endpackage -`endif //TEMPLATE_PKG_SV +`endif //TEMPLATE_PKG_SV diff --git a/source_code/packages/risc_mgmt/test_pkg.sv b/source_code/packages/risc_mgmt/test_pkg.sv index 18309f853..863ae4bfe 100644 --- a/source_code/packages/risc_mgmt/test_pkg.sv +++ b/source_code/packages/risc_mgmt/test_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: ISA extension used for the RISC-MGMT testbench +* Description: ISA extension used for the RISC-MGMT testbench */ `ifndef TEST_PKG_SV @@ -27,49 +27,49 @@ package test_pkg; - // Interface between the decode and execute stage - // This must be named "decode_execute_t" - typedef struct packed { - logic rtype; - logic rtype_stall; - logic br_j; - logic mem_lw; - logic mem_sw; - logic exception; - logic nop; - logic [8:0] imm; - } decode_execute_t; + // Interface between the decode and execute stage + // This must be named "decode_execute_t" + typedef struct packed { + logic rtype; + logic rtype_stall; + logic br_j; + logic mem_lw; + logic mem_sw; + logic exception; + logic nop; + logic [8:0] imm; + } decode_execute_t; - // Interface between the execute and memory stage - // This must be named "execute_memory_t" - typedef struct packed { - logic mem_lw; - logic mem_sw; - logic nop; - logic exception; - logic [31:0] mem_addr; - logic [31:0] mem_store; - } execute_memory_t; + // Interface between the execute and memory stage + // This must be named "execute_memory_t" + typedef struct packed { + logic mem_lw; + logic mem_sw; + logic nop; + logic exception; + logic [31:0] mem_addr; + logic [31:0] mem_store; + } execute_memory_t; - typedef enum logic [3:0] { - RTYPE, - RTYPE_STALL_5, - BR_J, - MEM_LOAD, - MEM_STORE, - EXCEPTION, - NOP - } test_funct_t; + typedef enum logic [3:0] { + RTYPE, + RTYPE_STALL_5, + BR_J, + MEM_LOAD, + MEM_STORE, + EXCEPTION, + NOP + } test_funct_t; - typedef struct packed { - logic [5:0] imm; - test_funct_t funct; - logic [4:0] rs_d; - logic [4:0] rs_0; - logic [4:0] rs_1; - logic [6:0] opcode; - } test_insn_t; + typedef struct packed { + logic [5:0] imm; + test_funct_t funct; + logic [4:0] rs_d; + logic [4:0] rs_0; + logic [4:0] rs_1; + logic [6:0] opcode; + } test_insn_t; endpackage -`endif //TEST_PKG_SV +`endif //TEST_PKG_SV diff --git a/source_code/packages/riscv_packages.core b/source_code/packages/riscv_packages.core new file mode 100644 index 000000000..362fef8b0 --- /dev/null +++ b/source_code/packages/riscv_packages.core @@ -0,0 +1,30 @@ +CAPI=2: +name: socet:riscv:packages:0.1.0 +description: RISC-V Tracker modules + +filesets: + rtl: + files: + - alu_types_pkg.sv + - rv32i_types_pkg.sv + - machine_mode_types_1_12_pkg.sv + - risc_mgmt/crc32_pkg.sv + - risc_mgmt/template_pkg.sv + - risc_mgmt/test_pkg.sv + - risc_mgmt/rv32m_pkg.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + lint: + filesets: + - rtl + description: Linting + default_tool: veriblelint + toplevel: alu_types_pkg + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search'] + diff --git a/source_code/packages/rv32i_types_pkg.sv b/source_code/packages/rv32i_types_pkg.sv index 92d79f295..bd8103cc4 100644 --- a/source_code/packages/rv32i_types_pkg.sv +++ b/source_code/packages/rv32i_types_pkg.sv @@ -1,22 +1,22 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: rv32i_types_pkg.sv -* -* Created by: Jacob R. Stevens +* +* Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 06/01/2016 * Description: Package containing types used for a RV32I implementation @@ -25,183 +25,183 @@ `ifndef RV32I_TYPES_PKG_SV `define RV32I_TYPES_PKG_SV package rv32i_types_pkg; - parameter WORD_SIZE = 32; - parameter RAM_ADDR_SIZE = 32; - parameter OP_W = 7; - parameter BR_W = 3; - parameter LD_W = 3; - parameter SW_W = 3; - parameter IMM_W = 3; - parameter REG_W = 3; - - typedef logic [WORD_SIZE-1:0] word_t; - - typedef enum logic [OP_W-1:0] { - LUI = 7'b0110111, - AUIPC = 7'b0010111, - JAL = 7'b1101111, - JALR = 7'b1100111, - // All branching instructions share an opcode - BRANCH = 7'b1100011, - // All load instructions share an opcode - LOAD = 7'b0000011, - // All store instructions share an opcode - STORE = 7'b0100011, - // All immediate ALU instructions share an opcode - IMMED = 7'b0010011, - // All register-register instructions share an opcode - REGREG = 7'b0110011, - // All system instructions share an opcode - SYSTEM = 7'b1110011, - MISCMEM = 7'b0001111 - } opcode_t; - - typedef enum logic [BR_W-1:0] { - BEQ = 3'b000, - BNE = 3'b001, - BLT = 3'b100, - BGE = 3'b101, - BLTU = 3'b110, - BGEU = 3'b111 - } branch_t; - - typedef enum logic [LD_W-1:0] { - LB = 3'b000, - LH = 3'b001, - LW = 3'b010, - LBU = 3'b100, - LHU = 3'b101 - } load_t; - - typedef enum logic [SW_W-1:0] { - SB = 3'b000, - SH = 3'b001, - SW = 3'b010 - } store_t; - - typedef enum logic [IMM_W-1:0] { - ADDI = 3'b000, - SLTI = 3'b010, - SLTIU = 3'b011, - XORI = 3'b100, - ORI = 3'b110, - ANDI = 3'b111, - SLLI = 3'b001, - // Logical/Arithmetic based on bit 30 of instruction - // 0 / 1 - SRI = 3'b101 - } imm_t; - - typedef enum logic [REG_W-1:0] { - // Add/Sub based on bit 30 of instruction - // 0 / 1 - ADDSUB = 3'b000, - SLL = 3'b001, - SLT = 3'b010, - SLTU = 3'b011, - XOR = 3'b100, - // Logical/Arithmetic based on bit 30 of instruction - // 0 / 1 - SR = 3'b101, - OR = 3'b110, - AND = 3'b111 - } regreg_t; - - typedef enum logic [2:0] { - // Non CSR contains ECALL, EBREAK, and xRET instructions - // ECALL/EBREAK based on bit 20 of instruction - // 0 / 1 - // xRET based on bits 28 and 29 of instruction - PRIV = 3'b000, - CSRRW = 3'b001, - CSRRS = 3'b010, - CSRRC = 3'b011, - CSRRWI = 3'b101, - CSRRSI = 3'b110, - CSRRCI = 3'b111 - } rv32i_system_t; - - typedef enum logic [11:0] { - ECALL = 12'b0000000_00000, - EBREAK = 12'b0000000_00001, - MRET = 12'b0011000_00010, - SRET = 12'b0001000_00010, - URET = 12'b0000000_00010, - WFI = 12'b0001000_00101 - } priv_insn_t; - - typedef enum logic [2:0] { - FENCE = 3'b000, - FENCEI = 3'b001 - } rv32i_miscmem_t; - - typedef struct packed { - logic [6:0] funct7; - logic [4:0] rs2; - logic [4:0] rs1; - logic [2:0] funct3; - logic [4:0] rd; - opcode_t opcode; - } rtype_t; - - typedef struct packed { - logic [11:0] imm11_00; - logic [4:0] rs1; - logic [2:0] funct3; - logic [4:0] rd; - opcode_t opcode; - } itype_t; - - typedef struct packed { - logic [6:0] imm11_05; - logic [4:0] rs2; - logic [4:0] rs1; - logic [2:0] funct3; - logic [4:0] imm04_00; - opcode_t opcode; - } stype_t; - - typedef struct packed { - logic imm12; - logic [5:0] imm10_05; - logic [4:0] rs2; - logic [4:0] rs1; - logic [2:0] funct3; - logic [3:0] imm04_01; - logic imm11; - opcode_t opcode; - } sbtype_t; - - typedef struct packed { - logic [19:0] imm31_12; - logic [4:0] rd; - opcode_t opcode; - } utype_t; - - typedef struct packed { - logic imm20; - logic [9:0] imm10_01; - logic imm11; - logic [7:0] imm19_12; - logic [4:0] rd; - opcode_t opcode; - } ujtype_t; - - typedef struct packed { - logic [11:0] csr; - logic [4:0] rs1_zimm; - logic [2:0] funct3; - logic [4:0] rd; - opcode_t opcode; - } systype_t; - - typedef struct packed { - logic token; - word_t pc; - word_t pc4; - word_t instr; - word_t prediction; - } fetch_ex_pipeline_reg_t; + parameter int WORD_SIZE = 32; + parameter int RAM_ADDR_SIZE = 32; + parameter int OP_W = 7; + parameter int BR_W = 3; + parameter int LD_W = 3; + parameter int SW_W = 3; + parameter int IMM_W = 3; + parameter int REG_W = 3; + + typedef logic [WORD_SIZE-1:0] word_t; + + typedef enum logic [OP_W-1:0] { + LUI = 7'b0110111, + AUIPC = 7'b0010111, + JAL = 7'b1101111, + JALR = 7'b1100111, + // All branching instructions share an opcode + BRANCH = 7'b1100011, + // All load instructions share an opcode + LOAD = 7'b0000011, + // All store instructions share an opcode + STORE = 7'b0100011, + // All immediate ALU instructions share an opcode + IMMED = 7'b0010011, + // All register-register instructions share an opcode + REGREG = 7'b0110011, + // All system instructions share an opcode + SYSTEM = 7'b1110011, + MISCMEM = 7'b0001111 + } opcode_t; + + typedef enum logic [BR_W-1:0] { + BEQ = 3'b000, + BNE = 3'b001, + BLT = 3'b100, + BGE = 3'b101, + BLTU = 3'b110, + BGEU = 3'b111 + } branch_t; + + typedef enum logic [LD_W-1:0] { + LB = 3'b000, + LH = 3'b001, + LW = 3'b010, + LBU = 3'b100, + LHU = 3'b101 + } load_t; + + typedef enum logic [SW_W-1:0] { + SB = 3'b000, + SH = 3'b001, + SW = 3'b010 + } store_t; + + typedef enum logic [IMM_W-1:0] { + ADDI = 3'b000, + SLTI = 3'b010, + SLTIU = 3'b011, + XORI = 3'b100, + ORI = 3'b110, + ANDI = 3'b111, + SLLI = 3'b001, + // Logical/Arithmetic based on bit 30 of instruction + // 0 / 1 + SRI = 3'b101 + } imm_t; + + typedef enum logic [REG_W-1:0] { + // Add/Sub based on bit 30 of instruction + // 0 / 1 + ADDSUB = 3'b000, + SLL = 3'b001, + SLT = 3'b010, + SLTU = 3'b011, + XOR = 3'b100, + // Logical/Arithmetic based on bit 30 of instruction + // 0 / 1 + SR = 3'b101, + OR = 3'b110, + AND = 3'b111 + } regreg_t; + + typedef enum logic [2:0] { + // Non CSR contains ECALL, EBREAK, and xRET instructions + // ECALL/EBREAK based on bit 20 of instruction + // 0 / 1 + // xRET based on bits 28 and 29 of instruction + PRIV = 3'b000, + CSRRW = 3'b001, + CSRRS = 3'b010, + CSRRC = 3'b011, + CSRRWI = 3'b101, + CSRRSI = 3'b110, + CSRRCI = 3'b111 + } rv32i_system_t; + + typedef enum logic [11:0] { + ECALL = 12'b0000000_00000, + EBREAK = 12'b0000000_00001, + MRET = 12'b0011000_00010, + SRET = 12'b0001000_00010, + URET = 12'b0000000_00010, + WFI = 12'b0001000_00101 + } priv_insn_t; + + typedef enum logic [2:0] { + FENCE = 3'b000, + FENCEI = 3'b001 + } rv32i_miscmem_t; + + typedef struct packed { + logic [6:0] funct7; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } rtype_t; + + typedef struct packed { + logic [11:0] imm11_00; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } itype_t; + + typedef struct packed { + logic [6:0] imm11_05; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] imm04_00; + opcode_t opcode; + } stype_t; + + typedef struct packed { + logic imm12; + logic [5:0] imm10_05; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [3:0] imm04_01; + logic imm11; + opcode_t opcode; + } sbtype_t; + + typedef struct packed { + logic [19:0] imm31_12; + logic [4:0] rd; + opcode_t opcode; + } utype_t; + + typedef struct packed { + logic imm20; + logic [9:0] imm10_01; + logic imm11; + logic [7:0] imm19_12; + logic [4:0] rd; + opcode_t opcode; + } ujtype_t; + + typedef struct packed { + logic [11:0] csr; + logic [4:0] rs1_zimm; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } systype_t; + + typedef struct packed { + logic token; + word_t pc; + word_t pc4; + word_t instr; + word_t prediction; + } fetch_ex_pipeline_reg_t; endpackage `endif diff --git a/source_code/packages/wscript b/source_code/packages/wscript deleted file mode 100644 index 1c24a2b98..000000000 --- a/source_code/packages/wscript +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(risc_mgmt) - -def sim_source(cnf): - cnf.recurse(risc_mgmt) diff --git a/source_code/pipelines/pipeline_wrapper.sv b/source_code/pipelines/pipeline_wrapper.sv index 9689f182c..58c046447 100644 --- a/source_code/pipelines/pipeline_wrapper.sv +++ b/source_code/pipelines/pipeline_wrapper.sv @@ -1,21 +1,21 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: pipeline_wrapper.sv -* +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 05/17/2017 @@ -32,17 +32,18 @@ `include "rv32c_if.vh" module pipeline_wrapper ( - input logic CLK, nRST, - output logic halt, - generic_bus_if.cpu igen_bus_if, - generic_bus_if.cpu dgen_bus_if, - prv_pipeline_if prv_pipe_if, - predictor_pipeline_if predict_if, - risc_mgmt_if rm_if, - cache_control_if cc_if, - sparce_pipeline_if sparce_if + input logic CLK, + nRST, + output logic halt, + generic_bus_if.cpu igen_bus_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if prv_pipe_if, + predictor_pipeline_if predict_if, + risc_mgmt_if rm_if, + cache_control_if cc_if, + sparce_pipeline_if sparce_if ); - - tspp tspp_pipeline(.*); + + tspp tspp_pipeline (.*); endmodule diff --git a/source_code/pipelines/tspp/tspp.core b/source_code/pipelines/tspp/tspp.core new file mode 100644 index 000000000..e40b084b0 --- /dev/null +++ b/source_code/pipelines/tspp/tspp.core @@ -0,0 +1,29 @@ +CAPI=2: +name: socet:riscv:tspp:0.1.0 +description: Two-stage pipeline + +filesets: + rtl: + files: + - tspp.sv + - tspp_fetch_stage.sv + - tspp_execute_stage.sv + - tspp_hazard_unit.sv + file_type: systemVerilogSource + + +targets: + default: &default + filesets: + - rtl + toplevel: tspp + + lint: + <<: *default + description: Linting + default_tool: veriblelint + toplevel: tspp + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search', '--waiver_files=tspp.waiver'] + diff --git a/source_code/pipelines/tspp/tspp.sv b/source_code/pipelines/tspp/tspp.sv index f436c7787..68df83115 100644 --- a/source_code/pipelines/tspp/tspp.sv +++ b/source_code/pipelines/tspp/tspp.sv @@ -1,21 +1,21 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: tspp.sv -* +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 @@ -33,23 +33,24 @@ `include "rv32c_if.vh" module tspp ( - input logic CLK, nRST, - output logic halt, - generic_bus_if.cpu igen_bus_if, - generic_bus_if.cpu dgen_bus_if, - prv_pipeline_if prv_pipe_if, - predictor_pipeline_if predict_if, - risc_mgmt_if rm_if, - cache_control_if cc_if, - sparce_pipeline_if sparce_if + input logic CLK, + nRST, + output logic halt, + generic_bus_if.cpu igen_bus_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if prv_pipe_if, + predictor_pipeline_if predict_if, + risc_mgmt_if rm_if, + cache_control_if cc_if, + sparce_pipeline_if sparce_if ); - //interface instantiations - tspp_fetch_execute_if fetch_ex_if(); - tspp_hazard_unit_if hazard_if(); + //interface instantiations + tspp_fetch_execute_if fetch_ex_if (); + tspp_hazard_unit_if hazard_if (); - //module instantiations - tspp_fetch_stage fetch_stage_i (.*); - tspp_execute_stage execute_stage_i (.*); - tspp_hazard_unit hazard_unit_i (.*); + //module instantiations + tspp_fetch_stage fetch_stage_i (.*); + tspp_execute_stage execute_stage_i (.*); + tspp_hazard_unit hazard_unit_i (.*); endmodule diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index 6293f58ee..06ede4efc 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -35,403 +35,389 @@ `include "cache_control_if.vh" `include "rv32c_if.vh" -module tspp_execute_stage( - input logic CLK, nRST, - tspp_fetch_execute_if.execute fetch_ex_if, - tspp_hazard_unit_if.execute hazard_if, - predictor_pipeline_if.update predict_if, - generic_bus_if.cpu dgen_bus_if, - prv_pipeline_if.pipe prv_pipe_if, - output logic halt, - risc_mgmt_if.ts_execute rm_if, - cache_control_if.pipeline cc_if, - sparce_pipeline_if.pipe_execute sparce_if, - rv32c_if.execute rv32cif, - output logic wfi +module tspp_execute_stage ( + input logic CLK, + nRST, + tspp_fetch_execute_if.execute fetch_ex_if, + tspp_hazard_unit_if.execute hazard_if, + predictor_pipeline_if.update predict_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if.pipe prv_pipe_if, + output logic halt, + risc_mgmt_if.ts_execute rm_if, + cache_control_if.pipeline cc_if, + sparce_pipeline_if.pipe_execute sparce_if, + rv32c_if.execute rv32cif, + output logic wfi ); - import rv32i_types_pkg::*; - - // Interface declarations - control_unit_if cu_if(); - rv32i_reg_file_if rf_if(); - alu_if alu_if(); - jump_calc_if jump_if(); - branch_res_if branch_if(); - - // Module instantiations - control_unit cu ( - .cu_if(cu_if), - .rf_if(rf_if), - .rmgmt_rsel_s_0(rm_if.rsel_s_0), - .rmgmt_rsel_s_1(rm_if.rsel_s_1), - .rmgmt_rsel_d(rm_if.rsel_d), - .rmgmt_req_reg_r(rm_if.req_reg_r), - .rmgmt_req_reg_w(rm_if.req_reg_w) - ); - // generate - // case (BASE_ISA) - // "RV32I": rv32i_reg_file rf (CLK, nRST, rf_if); - // "RV32E": rv32e_reg_file rf (CLK, nRST, rf_if); - // endcase - // endgenerate - assign wfi = cu_if.wfi; //Added by rkannank - generate - if (BASE_ISA == "RV32E") begin: REG_FILE_SEL - rv32e_reg_file rf (CLK, nRST, rf_if); - end - else begin: REG_FILE_SEL - rv32i_reg_file rf (CLK, nRST, rf_if); - end - endgenerate - // rv32i_reg_file rf (.*); - alu alu (.*); - jump_calc jump_calc (.*); - - branch_res branch_res ( - .br_if(branch_if) - ); - - word_t store_swapped; - endian_swapper store_swap ( - .word_in(rf_if.rs2_data), - .word_out(store_swapped) - ); - - word_t dload_ext; - logic [3:0] byte_en, byte_en_temp, byte_en_standard; - dmem_extender dmem_ext ( - .dmem_in(dgen_bus_if.rdata), - .load_type(cu_if.load_type), - .byte_en(byte_en), - .ext_out(dload_ext) - ); - - - /******************************************************* + import rv32i_types_pkg::*; + + // Interface declarations + control_unit_if cu_if (); + rv32i_reg_file_if rf_if (); + alu_if alu_if (); + jump_calc_if jump_if (); + branch_res_if branch_if (); + + // Module instantiations + control_unit cu ( + .cu_if(cu_if), + .rf_if(rf_if), + .rmgmt_rsel_s_0(rm_if.rsel_s_0), + .rmgmt_rsel_s_1(rm_if.rsel_s_1), + .rmgmt_rsel_d(rm_if.rsel_d), + .rmgmt_req_reg_r(rm_if.req_reg_r), + .rmgmt_req_reg_w(rm_if.req_reg_w) + ); + + assign wfi = cu_if.wfi; //Added by rkannank + + generate + if (BASE_ISA == "RV32E") begin : g_rfile_select + rv32e_reg_file rf ( + .CLK, + .nRST, + .rf_if + ); + end else begin : g_rfile_select + rv32i_reg_file rf ( + .CLK, + .nRST, + .rf_if + ); + end + endgenerate + // rv32i_reg_file rf (.*); + alu alu (.*); + jump_calc jump_calc (.*); + + branch_res branch_res (.br_if(branch_if)); + + word_t store_swapped; + endian_swapper store_swap ( + .word_in (rf_if.rs2_data), + .word_out(store_swapped) + ); + + word_t dload_ext; + logic [3:0] byte_en, byte_en_temp, byte_en_standard; + dmem_extender dmem_ext ( + .dmem_in (dgen_bus_if.rdata), + .load_type(cu_if.load_type), + .byte_en (byte_en), + .ext_out (dload_ext) + ); + + + /******************************************************* * MISC RISC-MGMT Logic *******************************************************/ - assign rm_if.rdata_s_0 = rf_if.rs1_data; - assign rm_if.rdata_s_1 = rf_if.rs2_data; + assign rm_if.rdata_s_0 = rf_if.rs1_data; + assign rm_if.rdata_s_1 = rf_if.rs2_data; - /******************************************************** + /******************************************************** *** Choose the Endianness Coming into the processor *******************************************************/ - generate - if (BUS_ENDIANNESS == "big") - begin - assign byte_en = byte_en_temp; - end else if (BUS_ENDIANNESS == "little") - begin - assign byte_en = cu_if.dren ? byte_en_temp : + generate + if (BUS_ENDIANNESS == "big") begin : g_data_bus_be + assign byte_en = byte_en_temp; + end else if (BUS_ENDIANNESS == "little") begin : g_data_bus_le + assign byte_en = cu_if.dren ? byte_en_temp : {byte_en_temp[0], byte_en_temp[1], byte_en_temp[2], byte_en_temp[3]}; - end - endgenerate + end + endgenerate - //RV32C - assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; - assign rv32cif.halt = cu_if.halt; - assign rv32cif.ex_busy = cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; - assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; - assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + //RV32C + assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; + assign rv32cif.halt = cu_if.halt; + assign rv32cif.ex_busy = cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; + assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; - /******************************************************* + /******************************************************* *** Sign Extensions *******************************************************/ - word_t imm_I_ext, imm_S_ext, imm_UJ_ext; - assign imm_I_ext = {{20{cu_if.imm_I[11]}}, cu_if.imm_I}; - assign imm_UJ_ext = {{11{cu_if.imm_UJ[20]}}, cu_if.imm_UJ}; - assign imm_S_ext = {{20{cu_if.imm_S[11]}}, cu_if.imm_S}; - - /******************************************************* - *** Jump Target Calculator and Associated Logic - *******************************************************/ - word_t jump_addr /* verilator isolate_assignments */; - always_comb begin - if (cu_if.j_sel) begin - jump_if.base = fetch_ex_if.fetch_ex_reg.pc; - jump_if.offset = imm_UJ_ext; - jump_addr = jump_if.jal_addr; - end else begin - jump_if.base = rf_if.rs1_data; - jump_if.offset = imm_I_ext; - jump_addr = jump_if.jalr_addr; + word_t imm_I_ext, imm_S_ext, imm_UJ_ext; + assign imm_I_ext = {{20{cu_if.imm_I[11]}}, cu_if.imm_I}; + assign imm_UJ_ext = {{11{cu_if.imm_UJ[20]}}, cu_if.imm_UJ}; + assign imm_S_ext = {{20{cu_if.imm_S[11]}}, cu_if.imm_S}; + + /******************************************************* + *** Jump Target Calculator and Associated Logic + *******************************************************/ + word_t jump_addr /* verilator isolate_assignments */; + always_comb begin + if (cu_if.j_sel) begin + jump_if.base = fetch_ex_if.fetch_ex_reg.pc; + jump_if.offset = imm_UJ_ext; + jump_addr = jump_if.jal_addr; + end else begin + jump_if.base = rf_if.rs1_data; + jump_if.offset = imm_I_ext; + jump_addr = jump_if.jalr_addr; + end end - end - /******************************************************* + /******************************************************* *** ALU and Associated Logic *******************************************************/ - word_t imm_or_shamt; - assign imm_or_shamt = (cu_if.imm_shamt_sel == 1'b1) ? cu_if.shamt : imm_I_ext; - assign alu_if.aluop = cu_if.alu_op; - logic mal_addr; - - always_comb begin - case (cu_if.alu_a_sel) - 2'd0: alu_if.port_a = rf_if.rs1_data; - 2'd1: alu_if.port_a = imm_S_ext; - 2'd2: alu_if.port_a = fetch_ex_if.fetch_ex_reg.pc; - 2'd3: alu_if.port_a = '0; //Not Used - endcase - end - - always_comb begin - case(cu_if.alu_b_sel) - 2'd0: alu_if.port_b = rf_if.rs1_data; - 2'd1: alu_if.port_b = rf_if.rs2_data; - 2'd2: alu_if.port_b = imm_or_shamt; - 2'd3: alu_if.port_b = cu_if.imm_U; - endcase - end - - always_comb begin - if(rm_if.req_reg_w) begin - rf_if.w_data = rm_if.reg_wdata; - end else begin - case(cu_if.w_sel) - 3'd0 : rf_if.w_data = dload_ext; - 3'd1 : rf_if.w_data = fetch_ex_if.fetch_ex_reg.pc4; - 3'd2 : rf_if.w_data = cu_if.imm_U; - 3'd3 : rf_if.w_data = alu_if.port_out; - 3'd4 : rf_if.w_data = prv_pipe_if.rdata; - default : rf_if.w_data = '0; - endcase + word_t imm_or_shamt; + assign imm_or_shamt = (cu_if.imm_shamt_sel == 1'b1) ? cu_if.shamt : imm_I_ext; + assign alu_if.aluop = cu_if.alu_op; + logic mal_addr; + + always_comb begin + case (cu_if.alu_a_sel) + 2'd0: alu_if.port_a = rf_if.rs1_data; + 2'd1: alu_if.port_a = imm_S_ext; + 2'd2: alu_if.port_a = fetch_ex_if.fetch_ex_reg.pc; + 2'd3: alu_if.port_a = '0; //Not Used + endcase + end + + always_comb begin + case (cu_if.alu_b_sel) + 2'd0: alu_if.port_b = rf_if.rs1_data; + 2'd1: alu_if.port_b = rf_if.rs2_data; + 2'd2: alu_if.port_b = imm_or_shamt; + 2'd3: alu_if.port_b = cu_if.imm_U; + endcase + end + + always_comb begin + if (rm_if.req_reg_w) begin + rf_if.w_data = rm_if.reg_wdata; + end else begin + case (cu_if.w_sel) + 3'd0: rf_if.w_data = dload_ext; + 3'd1: rf_if.w_data = fetch_ex_if.fetch_ex_reg.pc4; + 3'd2: rf_if.w_data = cu_if.imm_U; + 3'd3: rf_if.w_data = alu_if.port_out; + 3'd4: rf_if.w_data = prv_pipe_if.rdata; + default: rf_if.w_data = '0; + endcase + end end - end - assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) & (~hazard_if.if_ex_stall | hazard_if.npc_sel | rv32cif.done_earlier) & - ~(cu_if.dren & mal_addr); - /******************************************************* + assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) + & (~hazard_if.if_ex_stall | hazard_if.npc_sel | rv32cif.done_earlier) + & ~(cu_if.dren & mal_addr); + /******************************************************* *** Branch Target Resolution and Associated Logic *******************************************************/ - word_t resolved_addr; - logic branch_taken; - word_t branch_addr; + word_t resolved_addr; + logic branch_taken; + word_t branch_addr; - assign branch_if.rs1_data = rf_if.rs1_data; - assign branch_if.rs2_data = rf_if.rs2_data; - assign branch_if.pc = fetch_ex_if.fetch_ex_reg.pc; - assign branch_if.imm_sb = cu_if.imm_SB; - assign branch_if.branch_type = cu_if.branch_type; + assign branch_if.rs1_data = rf_if.rs1_data; + assign branch_if.rs2_data = rf_if.rs2_data; + assign branch_if.pc = fetch_ex_if.fetch_ex_reg.pc; + assign branch_if.imm_sb = cu_if.imm_SB; + assign branch_if.branch_type = cu_if.branch_type; - // Mux resource based on if RISC-MGMT is trying to access it - assign branch_taken = rm_if.req_br_j ? rm_if.branch_jump : branch_if.branch_taken; - assign branch_addr = rm_if.req_br_j ? rm_if.br_j_addr : branch_if.branch_addr; - assign rm_if.pc = fetch_ex_if.fetch_ex_reg.pc; + // Mux resource based on if RISC-MGMT is trying to access it + assign branch_taken = rm_if.req_br_j ? rm_if.branch_jump : branch_if.branch_taken; + assign branch_addr = rm_if.req_br_j ? rm_if.br_j_addr : branch_if.branch_addr; + assign rm_if.pc = fetch_ex_if.fetch_ex_reg.pc; - assign resolved_addr = branch_if.branch_taken ? - branch_addr : fetch_ex_if.fetch_ex_reg.pc4; + assign resolved_addr = branch_if.branch_taken ? branch_addr : fetch_ex_if.fetch_ex_reg.pc4; - assign fetch_ex_if.brj_addr = ((cu_if.ex_pc_sel == 1'b1) && ~rm_if.req_br_j) ? + assign fetch_ex_if.brj_addr = ((cu_if.ex_pc_sel == 1'b1) && ~rm_if.req_br_j) ? jump_addr : resolved_addr; - assign hazard_if.mispredict = fetch_ex_if.fetch_ex_reg.prediction ^ - branch_taken; + assign hazard_if.mispredict = fetch_ex_if.fetch_ex_reg.prediction ^ branch_taken; - /******************************************************* + /******************************************************* *** Data Ram Interface Logic *******************************************************/ - logic [1:0] byte_offset; - - // RISC-MGMT connection - assign rm_if.mem_load = dgen_bus_if.rdata; - - assign dgen_bus_if.ren = rm_if.req_mem ? rm_if.mem_ren : cu_if.dren & ~mal_addr; - assign dgen_bus_if.wen = rm_if.req_mem ? rm_if.mem_wen : cu_if.dwen & ~mal_addr; - assign byte_en_temp = rm_if.req_mem ? rm_if.mem_byte_en : byte_en_standard; - assign dgen_bus_if.byte_en = byte_en; - assign dgen_bus_if.addr = rm_if.req_mem ? rm_if.mem_addr : alu_if.port_out; - assign hazard_if.d_mem_busy = dgen_bus_if.busy; - assign byte_offset = alu_if.port_out[1:0]; - - always_comb begin - dgen_bus_if.wdata = '0; - if (rm_if.req_mem) - dgen_bus_if.wdata = rm_if.mem_store; - else begin - case(cu_if.load_type) // load_type can be used for store_type as well - LB: dgen_bus_if.wdata = {4{rf_if.rs2_data[7:0]}}; - LH: dgen_bus_if.wdata = {2{rf_if.rs2_data[15:0]}}; - LW: dgen_bus_if.wdata = rf_if.rs2_data; - endcase + logic [1:0] byte_offset; + + // RISC-MGMT connection + assign rm_if.mem_load = dgen_bus_if.rdata; + + assign dgen_bus_if.ren = rm_if.req_mem ? rm_if.mem_ren : cu_if.dren & ~mal_addr; + assign dgen_bus_if.wen = rm_if.req_mem ? rm_if.mem_wen : cu_if.dwen & ~mal_addr; + assign byte_en_temp = rm_if.req_mem ? rm_if.mem_byte_en : byte_en_standard; + assign dgen_bus_if.byte_en = byte_en; + assign dgen_bus_if.addr = rm_if.req_mem ? rm_if.mem_addr : alu_if.port_out; + assign hazard_if.d_mem_busy = dgen_bus_if.busy; + assign byte_offset = alu_if.port_out[1:0]; + + always_comb begin + dgen_bus_if.wdata = '0; + if (rm_if.req_mem) dgen_bus_if.wdata = rm_if.mem_store; + else begin + case (cu_if.load_type) // load_type can be used for store_type as well + LB: dgen_bus_if.wdata = {4{rf_if.rs2_data[7:0]}}; + LH: dgen_bus_if.wdata = {2{rf_if.rs2_data[15:0]}}; + LW: dgen_bus_if.wdata = rf_if.rs2_data; + endcase + end end - end - - - // Assign byte_en based on load type - // funct3 for loads and stores are the same bit positions - // byte_en is valid for both loads and stores - always_comb begin - unique case(cu_if.load_type) - LB : begin - unique case(byte_offset) - 2'b00 : byte_en_standard = 4'b0001; - 2'b01 : byte_en_standard = 4'b0010; - 2'b10 : byte_en_standard = 4'b0100; - 2'b11 : byte_en_standard = 4'b1000; - default : byte_en_standard = 4'b0000; - endcase - end - LBU : begin - unique case(byte_offset) - 2'b00 : byte_en_standard = 4'b0001; - 2'b01 : byte_en_standard = 4'b0010; - 2'b10 : byte_en_standard = 4'b0100; - 2'b11 : byte_en_standard = 4'b1000; - default : byte_en_standard = 4'b0000; - endcase - end - LH : begin - unique case(byte_offset) - 2'b00 : byte_en_standard = 4'b0011; - 2'b10 : byte_en_standard = 4'b1100; - default : byte_en_standard = 4'b0000; - endcase - end - LHU : begin - unique case(byte_offset) - 2'b00 : byte_en_standard = 4'b0011; - 2'b10 : byte_en_standard = 4'b1100; - default : byte_en_standard = 4'b0000; + + + // Assign byte_en based on load type + // funct3 for loads and stores are the same bit positions + // byte_en is valid for both loads and stores + always_comb begin + unique case (cu_if.load_type) + LB: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0001; + 2'b01: byte_en_standard = 4'b0010; + 2'b10: byte_en_standard = 4'b0100; + 2'b11: byte_en_standard = 4'b1000; + default: byte_en_standard = 4'b0000; + endcase + end + LBU: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0001; + 2'b01: byte_en_standard = 4'b0010; + 2'b10: byte_en_standard = 4'b0100; + 2'b11: byte_en_standard = 4'b1000; + default: byte_en_standard = 4'b0000; + endcase + end + LH: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0011; + 2'b10: byte_en_standard = 4'b1100; + default: byte_en_standard = 4'b0000; + endcase + end + LHU: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0011; + 2'b10: byte_en_standard = 4'b1100; + default: byte_en_standard = 4'b0000; + endcase + end + LW: byte_en_standard = 4'b1111; + default: byte_en_standard = 4'b0000; endcase - end - LW: byte_en_standard = 4'b1111; - default : byte_en_standard = 4'b0000; - endcase - end - - // Fence instructions - - // posedge detector for ifence - // subsequent ifences will have same effect as a single fence - logic ifence_reg; - logic ifence_pulse; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - ifence_reg <= 1'b0; - else - ifence_reg <= cu_if.ifence; - end - - assign ifence_pulse = cu_if.ifence && ~ifence_reg; - assign cc_if.icache_flush = ifence_pulse; - assign cc_if.icache_clear = 1'b0; - assign cc_if.dcache_flush = ifence_pulse; - assign cc_if.dcache_clear = 1'b0; - - //regs to detect flush completion - logic dflushed, iflushed; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - iflushed <= 1'b1; - else if (ifence_pulse) - iflushed <= 1'b0; - else if (cc_if.iflush_done) - iflushed <= 1'b1; - end - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - dflushed <= 1'b1; - else if (ifence_pulse) - dflushed <= 1'b0; - else if (cc_if.dflush_done) - dflushed <= 1'b1; - end - - assign hazard_if.fence_stall = cu_if.ifence && (~dflushed || ~iflushed); - - /******************************************************* + end + + // Fence instructions + + // posedge detector for ifence + // subsequent ifences will have same effect as a single fence + logic ifence_reg; + logic ifence_pulse; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) ifence_reg <= 1'b0; + else ifence_reg <= cu_if.ifence; + end + + assign ifence_pulse = cu_if.ifence && ~ifence_reg; + assign cc_if.icache_flush = ifence_pulse; + assign cc_if.icache_clear = 1'b0; + assign cc_if.dcache_flush = ifence_pulse; + assign cc_if.dcache_clear = 1'b0; + + //regs to detect flush completion + logic dflushed, iflushed; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) iflushed <= 1'b1; + else if (ifence_pulse) iflushed <= 1'b0; + else if (cc_if.iflush_done) iflushed <= 1'b1; + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) dflushed <= 1'b1; + else if (ifence_pulse) dflushed <= 1'b0; + else if (cc_if.dflush_done) dflushed <= 1'b1; + end + + assign hazard_if.fence_stall = cu_if.ifence && (~dflushed || ~iflushed); + + /******************************************************* *** Hazard Unit Interface Logic *******************************************************/ - assign hazard_if.dren = cu_if.dren; - assign hazard_if.dwen = cu_if.dwen; - assign hazard_if.jump = cu_if.jump; - assign hazard_if.branch = cu_if.branch; - assign hazard_if.halt = halt; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - halt <= 1'b0; - else if (cu_if.halt) - halt <= cu_if.halt; - end - - /******************************************************* + assign hazard_if.dren = cu_if.dren; + assign hazard_if.dwen = cu_if.dwen; + assign hazard_if.jump = cu_if.jump; + assign hazard_if.branch = cu_if.branch; + assign hazard_if.halt = halt; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) halt <= 1'b0; + else if (cu_if.halt) halt <= cu_if.halt; + end + + /******************************************************* *** CSR / Priv Interface Logic *******************************************************/ - assign prv_pipe_if.swap = cu_if.csr_swap; - assign prv_pipe_if.clr = cu_if.csr_clr; - assign prv_pipe_if.set = cu_if.csr_set; - assign prv_pipe_if.wdata = cu_if.csr_imm ? {27'h0, cu_if.zimm} : rf_if.rs1_data; - assign prv_pipe_if.csr_addr = cu_if.csr_addr; - assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr | + assign prv_pipe_if.swap = cu_if.csr_swap; + assign prv_pipe_if.clr = cu_if.csr_clr; + assign prv_pipe_if.set = cu_if.csr_set; + assign prv_pipe_if.wdata = cu_if.csr_imm ? {27'h0, cu_if.zimm} : rf_if.rs1_data; + assign prv_pipe_if.csr_addr = cu_if.csr_addr; + assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr | prv_pipe_if.set) & ~hazard_if.if_ex_stall; - assign prv_pipe_if.instr = (cu_if.instr != '0); + assign prv_pipe_if.instr = (cu_if.instr != '0); - always_comb begin - if(byte_en == 4'hf) - mal_addr = (dgen_bus_if.addr[1:0] != 2'b00); - else if (byte_en == 4'h3 || byte_en == 4'hc) begin - mal_addr = (dgen_bus_if.addr[1:0] == 2'b01 || dgen_bus_if.addr[1:0] == 2'b11); + always_comb begin + if (byte_en == 4'hf) mal_addr = (dgen_bus_if.addr[1:0] != 2'b00); + else if (byte_en == 4'h3 || byte_en == 4'hc) begin + mal_addr = (dgen_bus_if.addr[1:0] == 2'b01 || dgen_bus_if.addr[1:0] == 2'b11); + end else mal_addr = 1'b0; end - else - mal_addr = 1'b0; - end - - //Send exceptions to Hazard Unit - assign hazard_if.illegal_insn = (cu_if.illegal_insn & ~rm_if.ex_token) | prv_pipe_if.invalid_csr; - assign hazard_if.fault_l = 1'b0; - assign hazard_if.mal_l = cu_if.dren & mal_addr; - assign hazard_if.fault_s = 1'b0; - assign hazard_if.mal_s = cu_if.dwen & mal_addr; - assign hazard_if.breakpoint = cu_if.breakpoint; - assign hazard_if.env_m = cu_if.ecall_insn; - assign hazard_if.ret = cu_if.ret_insn; - assign hazard_if.badaddr_e = dgen_bus_if.addr; - - assign hazard_if.epc_e = fetch_ex_if.fetch_ex_reg.pc; - assign hazard_if.token_ex = fetch_ex_if.fetch_ex_reg.token; - - /********************************************************* + + //Send exceptions to Hazard Unit + assign hazard_if.illegal_insn = (cu_if.illegal_insn & ~rm_if.ex_token) + | prv_pipe_if.invalid_csr; + assign hazard_if.fault_l = 1'b0; + assign hazard_if.mal_l = cu_if.dren & mal_addr; + assign hazard_if.fault_s = 1'b0; + assign hazard_if.mal_s = cu_if.dwen & mal_addr; + assign hazard_if.breakpoint = cu_if.breakpoint; + assign hazard_if.env_m = cu_if.ecall_insn; + assign hazard_if.ret = cu_if.ret_insn; + assign hazard_if.badaddr_e = dgen_bus_if.addr; + + assign hazard_if.epc_e = fetch_ex_if.fetch_ex_reg.pc; + assign hazard_if.token_ex = fetch_ex_if.fetch_ex_reg.token; + + /********************************************************* *** Branch Predictor Logic *********************************************************/ - assign predict_if.update_predictor = cu_if.branch; - assign predict_if.prediction = fetch_ex_if.fetch_ex_reg.prediction; - assign predict_if.branch_result = branch_if.branch_taken; - //predict_if.update_addr = ; + assign predict_if.update_predictor = cu_if.branch; + assign predict_if.prediction = fetch_ex_if.fetch_ex_reg.prediction; + assign predict_if.branch_result = branch_if.branch_taken; + //predict_if.update_addr = ; - /********************************************************* + /********************************************************* *** SparCE Module Logic *********************************************************/ - assign sparce_if.wb_data = rf_if.w_data; - assign sparce_if.wb_en = rf_if.wen; - assign sparce_if.sasa_data = rf_if.rs2_data; - assign sparce_if.sasa_addr = alu_if.port_out; - assign sparce_if.sasa_wen = cu_if.dwen; - assign sparce_if.rd = rf_if.rd; - - /********************************************************* + assign sparce_if.wb_data = rf_if.w_data; + assign sparce_if.wb_en = rf_if.wen; + assign sparce_if.sasa_data = rf_if.rs2_data; + assign sparce_if.sasa_addr = alu_if.port_out; + assign sparce_if.sasa_wen = cu_if.dwen; + assign sparce_if.rd = rf_if.rd; + + /********************************************************* *** Signals for Bind Tracking - Read-Only, These don't affect execution *********************************************************/ - logic wb_stall; - logic [2:0] funct3; - logic [11:0] funct12; - logic instr_30; - - assign wb_stall = hazard_if.if_ex_stall & ~hazard_if.jump & ~hazard_if.branch; - assign funct3 = cu_if.instr[14:12]; - assign funct12 = cu_if.instr[31:20]; - assign instr_30 = cu_if.instr[30]; + logic wb_stall; + logic [2:0] funct3; + logic [11:0] funct12; + logic instr_30; + + assign wb_stall = hazard_if.if_ex_stall & ~hazard_if.jump & ~hazard_if.branch; + assign funct3 = cu_if.instr[14:12]; + assign funct12 = cu_if.instr[31:20]; + assign instr_30 = cu_if.instr[30]; endmodule diff --git a/source_code/pipelines/tspp/tspp_fetch_stage.sv b/source_code/pipelines/tspp/tspp_fetch_stage.sv index 6264004d9..1accd5354 100644 --- a/source_code/pipelines/tspp/tspp_fetch_stage.sv +++ b/source_code/pipelines/tspp/tspp_fetch_stage.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -31,93 +31,101 @@ `include "rv32c_if.vh" module tspp_fetch_stage ( - input logic CLK, nRST, - tspp_fetch_execute_if.fetch fetch_ex_if, - tspp_hazard_unit_if.fetch hazard_if, - predictor_pipeline_if.access predict_if, - generic_bus_if.cpu igen_bus_if, - sparce_pipeline_if.pipe_fetch sparce_if, - rv32c_if.fetch rv32cif + input logic CLK, + nRST, + tspp_fetch_execute_if.fetch fetch_ex_if, + tspp_hazard_unit_if.fetch hazard_if, + predictor_pipeline_if.access predict_if, + generic_bus_if.cpu igen_bus_if, + sparce_pipeline_if.pipe_fetch sparce_if, + rv32c_if.fetch rv32cif ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; + + parameter logic [31:0] RESET_PC = 32'h80000000; + + word_t pc, pc4or2, npc, instr; + + //PC logic - //parameter RESET_PC = 32'h200; - parameter RESET_PC = 32'h80000000; + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + pc <= RESET_PC; + end else if (hazard_if.pc_en | rv32cif.done_earlier) begin + pc <= npc; + end + end + + //RV32C + assign rv32cif.inst = igen_bus_if.rdata; + assign rv32cif.inst_arrived = hazard_if.if_ex_flush == 0 & hazard_if.if_ex_stall == 0; + assign rv32cif.reset_en = hazard_if.insert_priv_pc | sparce_if.skipping + | hazard_if.npc_sel | predict_if.predict_taken; + assign rv32cif.pc_update = hazard_if.pc_en; + assign rv32cif.reset_pc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (sparce_if.skipping ? sparce_if.sparce_target + : (hazard_if.npc_sel ? fetch_ex_if.brj_addr + : (predict_if.predict_taken ? predict_if.target_addr + : pc4or2))); + assign rv32cif.reset_pc_val = RESET_PC; - word_t pc, pc4or2, npc, instr; + assign pc4or2 = (rv32cif.rv32c_ena & (rv32cif.result[1:0] != 2'b11)) ? (pc + 2) : (pc + 4); + assign predict_if.current_pc = pc; + assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (sparce_if.skipping ? sparce_if.sparce_target + : (hazard_if.npc_sel ? fetch_ex_if.brj_addr + : (predict_if.predict_taken ? predict_if.target_addr + : rv32cif.rv32c_ena ? rv32cif.nextpc + : pc4or2))); - //PC logic + //Instruction Access logic + assign hazard_if.i_mem_busy = igen_bus_if.busy; + assign igen_bus_if.addr = rv32cif.rv32c_ena ? rv32cif.imem_pc : pc; + assign igen_bus_if.ren = hazard_if.iren & !rv32cif.done_earlier; + assign igen_bus_if.wen = 1'b0; + assign igen_bus_if.byte_en = 4'b1111; + assign igen_bus_if.wdata = '0; - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) begin - pc <= RESET_PC; - end else if (hazard_if.pc_en | rv32cif.done_earlier) begin - pc <= npc; + //Fetch Execute Pipeline Signals + word_t instr_to_ex; + assign instr_to_ex = rv32cif.rv32c_ena ? rv32cif.result : igen_bus_if.rdata; + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) fetch_ex_if.fetch_ex_reg <= '0; + else if (hazard_if.if_ex_flush) fetch_ex_if.fetch_ex_reg <= '0; + else if (((rv32cif.done | rv32cif.done_earlier) & rv32cif.rv32c_ena) + | (!hazard_if.if_ex_stall & !rv32cif.rv32c_ena)) begin + fetch_ex_if.fetch_ex_reg.token <= 1'b1; + fetch_ex_if.fetch_ex_reg.pc <= pc; + fetch_ex_if.fetch_ex_reg.pc4 <= pc4or2; + fetch_ex_if.fetch_ex_reg.instr <= instr_to_ex; + fetch_ex_if.fetch_ex_reg.prediction <= predict_if.predict_taken; + end end - end - - //RV32C - assign rv32cif.inst = igen_bus_if.rdata; - assign rv32cif.inst_arrived = hazard_if.if_ex_flush == 0 & hazard_if.if_ex_stall == 0; - assign rv32cif.reset_en = hazard_if.insert_priv_pc | sparce_if.skipping | hazard_if.npc_sel | predict_if.predict_taken; - assign rv32cif.pc_update = hazard_if.pc_en; - assign rv32cif.reset_pc = npc; - assign rv32cif.reset_pc_val = RESET_PC; - - assign pc4or2 = (rv32cif.rv32c_ena & (rv32cif.result[1:0] != 2'b11)) ? (pc + 2) : (pc + 4); - assign predict_if.current_pc = pc; - assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc : ( sparce_if.skipping ? sparce_if.sparce_target : (hazard_if.npc_sel ? fetch_ex_if.brj_addr : - (predict_if.predict_taken ? predict_if.target_addr : rv32cif.rv32c_ena ? rv32cif.nextpc : pc4or2))); - - //Instruction Access logic - assign hazard_if.i_mem_busy = igen_bus_if.busy; - assign igen_bus_if.addr = rv32cif.rv32c_ena ? rv32cif.imem_pc : pc; - assign igen_bus_if.ren = hazard_if.iren & !rv32cif.done_earlier; - assign igen_bus_if.wen = 1'b0; - assign igen_bus_if.byte_en = 4'b1111; - assign igen_bus_if.wdata = '0; - - assign hazard_if.rv32c_ready = rv32cif.done_earlier; - - //Fetch Execute Pipeline Signals - word_t instr_to_ex; - assign instr_to_ex = rv32cif.rv32c_ena ? rv32cif.result : igen_bus_if.rdata; - always_ff @ (posedge CLK, negedge nRST) begin - if (!nRST) - fetch_ex_if.fetch_ex_reg <= '0; - else if (hazard_if.if_ex_flush) - fetch_ex_if.fetch_ex_reg <= '0; - else if (((rv32cif.done | rv32cif.done_earlier) & rv32cif.rv32c_ena) | (!hazard_if.if_ex_stall & !rv32cif.rv32c_ena)) begin - fetch_ex_if.fetch_ex_reg.token <= 1'b1; - fetch_ex_if.fetch_ex_reg.pc <= pc; - fetch_ex_if.fetch_ex_reg.pc4 <= pc4or2; - fetch_ex_if.fetch_ex_reg.instr <= instr_to_ex; - fetch_ex_if.fetch_ex_reg.prediction <= predict_if.predict_taken; - end - end - - //Send exceptions to Hazard Unit - logic mal_addr; - assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); - assign hazard_if.fault_insn = 1'b0; - assign hazard_if.mal_insn = mal_addr; - assign hazard_if.badaddr_f = igen_bus_if.addr; - assign hazard_if.epc_f = pc; - - // Choose the endianness of the data coming into the processor - generate - if (BUS_ENDIANNESS == "big") - assign instr = igen_bus_if.rdata; - else if (BUS_ENDIANNESS == "little") - endian_swapper ltb_endian(igen_bus_if.rdata, instr); - endgenerate - - /********************************************************* - *** SparCE Module Logic - *********************************************************/ - - assign sparce_if.pc = pc; - assign sparce_if.rdata = igen_bus_if.rdata; + + //Send exceptions to Hazard Unit + logic mal_addr; + assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); + assign hazard_if.fault_insn = 1'b0; + assign hazard_if.mal_insn = mal_addr; + assign hazard_if.badaddr_f = igen_bus_if.addr; + assign hazard_if.epc_f = pc; + + // Choose the endianness of the data coming into the processor + generate + if (BUS_ENDIANNESS == "big") assign instr = igen_bus_if.rdata; + else if (BUS_ENDIANNESS == "little") + endian_swapper ltb_endian ( + .word_in(igen_bus_if.rdata), + .word_out(instr) + ); + endgenerate + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + + assign sparce_if.pc = pc; + assign sparce_if.rdata = igen_bus_if.rdata; endmodule diff --git a/source_code/pipelines/tspp/tspp_hazard_unit.sv b/source_code/pipelines/tspp/tspp_hazard_unit.sv index d8546bb9c..4d4f89164 100644 --- a/source_code/pipelines/tspp/tspp_hazard_unit.sv +++ b/source_code/pipelines/tspp/tspp_hazard_unit.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,100 +20,100 @@ * Email: steven69@purdue.edu * Date Created: 06/14/2016 * Description: Hazard unit that controls the flushing and stalling of -* the stages of the Two Stage Pipeline +* the stages of the Two Stage Pipeline */ `include "tspp_hazard_unit_if.vh" `include "prv_pipeline_if.vh" `include "risc_mgmt_if.vh" -module tspp_hazard_unit -( - tspp_hazard_unit_if.hazard_unit hazard_if, - prv_pipeline_if.hazard prv_pipe_if, - risc_mgmt_if.ts_hazard rm_if, - sparce_pipeline_if.hazard sparce_if +module tspp_hazard_unit ( + tspp_hazard_unit_if.hazard_unit hazard_if, + prv_pipeline_if.hazard prv_pipe_if, + risc_mgmt_if.ts_hazard rm_if, + sparce_pipeline_if.hazard sparce_if ); - import alu_types_pkg::*; - import rv32i_types_pkg::*; - - // Pipeline hazard signals - logic dmem_access; - logic branch_jump; - logic wait_for_imem; - logic wait_for_dmem; - - // IRQ/Exception hazard signals - logic ex_flush_hazard; - logic e_ex_stage; - logic e_f_stage; - logic intr; - - logic rmgmt_stall; - - assign rm_if.if_ex_enable = ~hazard_if.if_ex_stall; - assign rmgmt_stall = rm_if.memory_stall | rm_if.execute_stall; - - assign dmem_access = (hazard_if.dren || hazard_if.dwen); - assign branch_jump = hazard_if.jump || - (hazard_if.branch && hazard_if.mispredict); - assign wait_for_imem = hazard_if.iren & hazard_if.i_mem_busy & ~hazard_if.rv32c_ready; - assign wait_for_dmem = dmem_access & hazard_if.d_mem_busy; - - assign hazard_if.npc_sel = branch_jump; - - assign hazard_if.pc_en = (~wait_for_dmem&~wait_for_imem&~hazard_if.halt&~ex_flush_hazard&~rmgmt_stall&~hazard_if.fence_stall) | - branch_jump | prv_pipe_if.insert_pc | prv_pipe_if.ret; - - assign hazard_if.if_ex_flush = ex_flush_hazard | branch_jump | + import alu_types_pkg::*; + import rv32i_types_pkg::*; + + // Pipeline hazard signals + logic dmem_access; + logic branch_jump; + logic wait_for_imem; + logic wait_for_dmem; + + // IRQ/Exception hazard signals + logic ex_flush_hazard; + logic e_ex_stage; + logic e_f_stage; + logic intr; + + logic rmgmt_stall; + + assign rm_if.if_ex_enable = ~hazard_if.if_ex_stall; + assign rmgmt_stall = rm_if.memory_stall | rm_if.execute_stall; + + assign dmem_access = (hazard_if.dren || hazard_if.dwen); + assign branch_jump = hazard_if.jump || (hazard_if.branch && hazard_if.mispredict); + assign wait_for_imem = hazard_if.iren & hazard_if.i_mem_busy; + assign wait_for_dmem = dmem_access & hazard_if.d_mem_busy; + + assign hazard_if.npc_sel = branch_jump; + + assign hazard_if.pc_en = (~wait_for_dmem & ~wait_for_imem & ~hazard_if.halt & ~ex_flush_hazard + & ~rmgmt_stall & ~hazard_if.fence_stall) + | branch_jump | prv_pipe_if.insert_pc | prv_pipe_if.ret; + + assign hazard_if.if_ex_flush = ex_flush_hazard | branch_jump | (wait_for_imem & dmem_access & ~hazard_if.d_mem_busy); - assign hazard_if.if_ex_stall = (wait_for_dmem || (wait_for_imem & ~dmem_access) || hazard_if.halt) - & (~ex_flush_hazard | e_ex_stage) - || hazard_if.fence_stall - || rm_if.execute_stall; + assign hazard_if.if_ex_stall = (wait_for_dmem || + (wait_for_imem & ~dmem_access) || + hazard_if.halt) & (~ex_flush_hazard | e_ex_stage) || + hazard_if.fence_stall || + rm_if.execute_stall; - /* Hazards due to Interrupts/Exceptions */ - assign prv_pipe_if.ret = hazard_if.ret; - assign e_ex_stage = hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l | + /* Hazards due to Interrupts/Exceptions */ + assign prv_pipe_if.ret = hazard_if.ret; + assign e_ex_stage = hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l | hazard_if.fault_s | hazard_if.mal_s | hazard_if.breakpoint | hazard_if.env_m; - assign e_f_stage = hazard_if.fault_insn | hazard_if.mal_insn; - assign intr = ~e_ex_stage & ~e_f_stage & prv_pipe_if.intr; + assign e_f_stage = hazard_if.fault_insn | hazard_if.mal_insn; + assign intr = ~e_ex_stage & ~e_f_stage & prv_pipe_if.intr; - assign prv_pipe_if.pipe_clear = e_ex_stage | ~(hazard_if.token_ex | rm_if.active_insn); - assign ex_flush_hazard = ((intr | e_f_stage) & ~wait_for_dmem) | e_ex_stage | prv_pipe_if.ret; + assign prv_pipe_if.pipe_clear = e_ex_stage | ~(hazard_if.token_ex | rm_if.active_insn); + assign ex_flush_hazard = ((intr | e_f_stage) & ~wait_for_dmem) | e_ex_stage | prv_pipe_if.ret; - assign hazard_if.insert_priv_pc = prv_pipe_if.insert_pc; - assign hazard_if.priv_pc = prv_pipe_if.priv_pc; - - assign hazard_if.iren = !intr; // prevents a false instruction request from being sent + assign hazard_if.insert_priv_pc = prv_pipe_if.insert_pc; + assign hazard_if.priv_pc = prv_pipe_if.priv_pc; - /* Send Exception notifications to Prv Block */ + assign hazard_if.iren = !intr; // prevents a false instruction request from being sent - assign prv_pipe_if.wb_enable = !hazard_if.if_ex_stall | + /* Send Exception notifications to Prv Block */ + + assign prv_pipe_if.wb_enable = !hazard_if.if_ex_stall | hazard_if.jump | hazard_if.branch; //Because 2 stages - assign prv_pipe_if.fault_insn = hazard_if.fault_insn; - assign prv_pipe_if.mal_insn = hazard_if.mal_insn; - assign prv_pipe_if.illegal_insn = hazard_if.illegal_insn; - assign prv_pipe_if.fault_l = hazard_if.fault_l; - assign prv_pipe_if.mal_l = hazard_if.mal_l; - assign prv_pipe_if.fault_s = hazard_if.fault_s; - assign prv_pipe_if.mal_s = hazard_if.mal_s; - assign prv_pipe_if.breakpoint = hazard_if.breakpoint; - assign prv_pipe_if.env_m = hazard_if.env_m; - assign prv_pipe_if.ex_rmgmt = rm_if.exception; - - assign prv_pipe_if.ex_rmgmt_cause = rm_if.ex_cause; - assign prv_pipe_if.epc = (e_ex_stage | rm_if.exception) ? hazard_if.epc_e : hazard_if.epc_f; - assign prv_pipe_if.badaddr = (hazard_if.mal_insn | hazard_if.fault_insn) ? hazard_if.badaddr_f : - (rm_if.exception ? rm_if.mem_addr : hazard_if.badaddr_e); - - /********************************************************* + assign prv_pipe_if.fault_insn = hazard_if.fault_insn; + assign prv_pipe_if.mal_insn = hazard_if.mal_insn; + assign prv_pipe_if.illegal_insn = hazard_if.illegal_insn; + assign prv_pipe_if.fault_l = hazard_if.fault_l; + assign prv_pipe_if.mal_l = hazard_if.mal_l; + assign prv_pipe_if.fault_s = hazard_if.fault_s; + assign prv_pipe_if.mal_s = hazard_if.mal_s; + assign prv_pipe_if.breakpoint = hazard_if.breakpoint; + assign prv_pipe_if.env_m = hazard_if.env_m; + assign prv_pipe_if.ex_rmgmt = rm_if.exception; + + assign prv_pipe_if.ex_rmgmt_cause = rm_if.ex_cause; + assign prv_pipe_if.epc = (e_ex_stage | rm_if.exception) ? hazard_if.epc_e : hazard_if.epc_f; + assign prv_pipe_if.badaddr = (hazard_if.mal_insn | hazard_if.fault_insn) ? hazard_if.badaddr_f : + (rm_if.exception ? rm_if.mem_addr : hazard_if.badaddr_e); + + /********************************************************* *** SparCE Module Logic *********************************************************/ - assign sparce_if.if_ex_enable = rm_if.if_ex_enable; + assign sparce_if.if_ex_enable = rm_if.if_ex_enable; endmodule diff --git a/source_code/pipelines/wscript b/source_code/pipelines/wscript deleted file mode 100644 index 8eb0fdef4..000000000 --- a/source_code/pipelines/wscript +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(tspp) - -def sim_source(cnf): - cnf.recurse(tspp) diff --git a/source_code/privs/priv.core b/source_code/privs/priv.core new file mode 100644 index 000000000..edb154f5a --- /dev/null +++ b/source_code/privs/priv.core @@ -0,0 +1,28 @@ +CAPI=2: +name: socet:riscv:priv:0.1.0 +description: Privileged Architecture for RISCVBusiness + +filesets: + rtl: + files: + - priv_wrapper.sv + - priv_1_12/priv_1_12_int_ex_handler.sv + - priv_1_12/priv_1_12_block.sv + - priv_1_12/priv_1_12_csr.sv + - priv_1_12/priv_1_12_pipe_control.sv + file_type: systemVerilogSource + + +targets: + default: &default + filesets: + - rtl + toplevel: priv_wrapper + + lint: + <<: *default + description: Linting + default_tool: veriblelint + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search'] diff --git a/source_code/privs/priv_1_11/priv_1_11_block.sv b/source_code/privs/priv_1_11/priv_1_11_block.sv index 6c005aeb4..ab513a1a1 100644 --- a/source_code/privs/priv_1_11/priv_1_11_block.sv +++ b/source_code/privs/priv_1_11/priv_1_11_block.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,85 +27,95 @@ `include "core_interrupt_if.vh" module priv_1_11_block ( - input logic CLK, nRST, - prv_pipeline_if.priv_block prv_pipe_if, - core_interrupt_if.core interrupt_if - /*input logic plic_ext_int, + input logic CLK, + nRST, + prv_pipeline_if.priv_block prv_pipe_if, + core_interrupt_if.core interrupt_if + /*input logic plic_ext_int, input logic clint_soft_int, input logic clint_clear_soft_int, input logic clint_timer_int, input logic clint_clear_timer_int*/ ); - import machine_mode_types_1_11_pkg::*; + import machine_mode_types_1_11_pkg::*; + + priv_1_11_internal_if prv_intern_if (); + - priv_1_11_internal_if prv_intern_if(); + priv_1_11_csr_rfile csr_rfile_i ( + .*, + .prv_intern_if(prv_intern_if) + ); + priv_1_11_control prv_control_i ( + .*, + .prv_intern_if(prv_intern_if) + ); + priv_1_11_pipeline_control pipeline_control_i ( + .*, + .prv_intern_if(prv_intern_if) + ); - - priv_1_11_csr_rfile csr_rfile_i(.*, .prv_intern_if(prv_intern_if)); - priv_1_11_control prv_control_i(.*, .prv_intern_if(prv_intern_if)); - priv_1_11_pipeline_control pipeline_control_i(.*, .prv_intern_if(prv_intern_if)); + // Disable interrupts that will not be used + assign prv_intern_if.timer_int_u = 1'b0; + assign prv_intern_if.timer_int_s = 1'b0; + assign prv_intern_if.timer_int_m = interrupt_if.timer_int; + assign prv_intern_if.soft_int_u = 1'b0; + assign prv_intern_if.soft_int_s = 1'b0; + assign prv_intern_if.soft_int_m = interrupt_if.soft_int; + assign prv_intern_if.ext_int_u = 1'b0; + assign prv_intern_if.ext_int_s = 1'b0; + assign prv_intern_if.ext_int_m = interrupt_if.ext_int; + assign prv_intern_if.reserved_0 = 1'b0; + assign prv_intern_if.reserved_1 = 1'b0; + assign prv_intern_if.reserved_2 = 1'b0; - // Disable interrupts that will not be used - assign prv_intern_if.timer_int_u = 1'b0; - assign prv_intern_if.timer_int_s = 1'b0; - assign prv_intern_if.timer_int_m = interrupt_if.timer_int; - assign prv_intern_if.soft_int_u = 1'b0; - assign prv_intern_if.soft_int_s = 1'b0; - assign prv_intern_if.soft_int_m = interrupt_if.soft_int; - assign prv_intern_if.ext_int_u = 1'b0; - assign prv_intern_if.ext_int_s = 1'b0; - assign prv_intern_if.ext_int_m = interrupt_if.ext_int; - assign prv_intern_if.reserved_0 = 1'b0; - assign prv_intern_if.reserved_1 = 1'b0; - assign prv_intern_if.reserved_2 = 1'b0; + // Disable clear interrupts that will not be used + assign prv_intern_if.clear_timer_int_u = 1'b0; + assign prv_intern_if.clear_timer_int_s = 1'b0; + assign prv_intern_if.clear_timer_int_m = interrupt_if.timer_int_clear; + assign prv_intern_if.clear_soft_int_u = 1'b0; + assign prv_intern_if.clear_soft_int_s = 1'b0; + assign prv_intern_if.clear_soft_int_m = interrupt_if.soft_int_clear; + assign prv_intern_if.clear_ext_int_u = 1'b0; + assign prv_intern_if.clear_ext_int_s = 1'b0; + assign prv_intern_if.clear_ext_int_m = interrupt_if.ext_int_clear; - // Disable clear interrupts that will not be used - assign prv_intern_if.clear_timer_int_u = 1'b0; - assign prv_intern_if.clear_timer_int_s = 1'b0; - assign prv_intern_if.clear_timer_int_m = interrupt_if.timer_int_clear; - assign prv_intern_if.clear_soft_int_u = 1'b0; - assign prv_intern_if.clear_soft_int_s = 1'b0; - assign prv_intern_if.clear_soft_int_m = interrupt_if.soft_int_clear; - assign prv_intern_if.clear_ext_int_u = 1'b0; - assign prv_intern_if.clear_ext_int_s = 1'b0; - assign prv_intern_if.clear_ext_int_m = interrupt_if.ext_int_clear; + // from pipeline to the priv unit + assign prv_intern_if.pipe_clear = prv_pipe_if.pipe_clear; + assign prv_intern_if.mret = prv_pipe_if.ret; + assign prv_intern_if.epc = prv_pipe_if.epc; + assign prv_intern_if.fault_insn_access = prv_pipe_if.fault_insn; + assign prv_intern_if.mal_insn = prv_pipe_if.mal_insn; + assign prv_intern_if.illegal_insn = prv_pipe_if.illegal_insn; + assign prv_intern_if.fault_l = prv_pipe_if.fault_l; + assign prv_intern_if.mal_l = prv_pipe_if.mal_l; + assign prv_intern_if.fault_s = prv_pipe_if.fault_s; + assign prv_intern_if.mal_s = prv_pipe_if.mal_s; + assign prv_intern_if.breakpoint = prv_pipe_if.breakpoint; + assign prv_intern_if.env_m = prv_pipe_if.env_m; + assign prv_intern_if.env_s = 1'b0; + assign prv_intern_if.env_u = 1'b0; + assign prv_intern_if.fault_insn_page = 1'b0; + assign prv_intern_if.fault_load_page = 1'b0; + assign prv_intern_if.fault_store_page = 1'b0; + assign prv_intern_if.mtval = prv_pipe_if.badaddr; + assign prv_intern_if.swap = prv_pipe_if.swap; + assign prv_intern_if.clr = prv_pipe_if.clr; + assign prv_intern_if.set = prv_pipe_if.set; + assign prv_intern_if.wdata = prv_pipe_if.wdata; + assign prv_intern_if.addr = prv_pipe_if.addr; + assign prv_intern_if.valid_write = prv_pipe_if.valid_write; + assign prv_intern_if.instr_retired = prv_pipe_if.wb_enable & prv_pipe_if.instr; - // from pipeline to the priv unit - assign prv_intern_if.pipe_clear = prv_pipe_if.pipe_clear; - assign prv_intern_if.mret = prv_pipe_if.ret; - assign prv_intern_if.epc = prv_pipe_if.epc; - assign prv_intern_if.fault_insn_access = prv_pipe_if.fault_insn; - assign prv_intern_if.mal_insn = prv_pipe_if.mal_insn; - assign prv_intern_if.illegal_insn = prv_pipe_if.illegal_insn; - assign prv_intern_if.fault_l = prv_pipe_if.fault_l; - assign prv_intern_if.mal_l = prv_pipe_if.mal_l; - assign prv_intern_if.fault_s = prv_pipe_if.fault_s; - assign prv_intern_if.mal_s = prv_pipe_if.mal_s; - assign prv_intern_if.breakpoint = prv_pipe_if.breakpoint; - assign prv_intern_if.env_m = prv_pipe_if.env_m; - assign prv_intern_if.env_s = 1'b0; - assign prv_intern_if.env_u = 1'b0; - assign prv_intern_if.fault_insn_page = 1'b0; - assign prv_intern_if.fault_load_page = 1'b0; - assign prv_intern_if.fault_store_page = 1'b0; - assign prv_intern_if.mtval = prv_pipe_if.badaddr; - assign prv_intern_if.swap = prv_pipe_if.swap; - assign prv_intern_if.clr = prv_pipe_if.clr; - assign prv_intern_if.set = prv_pipe_if.set; - assign prv_intern_if.wdata = prv_pipe_if.wdata; - assign prv_intern_if.addr = prv_pipe_if.addr; - assign prv_intern_if.valid_write = prv_pipe_if.valid_write; - assign prv_intern_if.instr_retired= prv_pipe_if.wb_enable & prv_pipe_if.instr; + assign prv_intern_if.ex_rmgmt = prv_pipe_if.ex_rmgmt; + assign prv_intern_if.ex_rmgmt_cause = prv_pipe_if.ex_rmgmt_cause; - assign prv_intern_if.ex_rmgmt = prv_pipe_if.ex_rmgmt; - assign prv_intern_if.ex_rmgmt_cause = prv_pipe_if.ex_rmgmt_cause; + // from priv unit to pipeline + assign prv_pipe_if.priv_pc = prv_intern_if.priv_pc; + assign prv_pipe_if.insert_pc = prv_intern_if.insert_pc; + assign prv_pipe_if.intr = prv_intern_if.intr; + assign prv_pipe_if.rdata = prv_intern_if.rdata; + assign prv_pipe_if.invalid_csr = prv_intern_if.invalid_csr; - // from priv unit to pipeline - assign prv_pipe_if.priv_pc = prv_intern_if.priv_pc; - assign prv_pipe_if.insert_pc = prv_intern_if.insert_pc; - assign prv_pipe_if.intr = prv_intern_if.intr; - assign prv_pipe_if.rdata = prv_intern_if.rdata; - assign prv_pipe_if.invalid_csr = prv_intern_if.invalid_csr; - endmodule diff --git a/source_code/privs/priv_1_11/priv_1_11_control.sv b/source_code/privs/priv_1_11/priv_1_11_control.sv index 47aed5a7b..be6f96db2 100644 --- a/source_code/privs/priv_1_11/priv_1_11_control.sv +++ b/source_code/privs/priv_1_11/priv_1_11_control.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,176 +25,147 @@ `include "priv_1_11_internal_if.vh" module priv_1_11_control ( - input CLK, nRST, - priv_1_11_internal_if.prv_control prv_intern_if + input CLK, + nRST, + priv_1_11_internal_if.prv_control prv_intern_if ); - import rv32i_types_pkg::*; - import machine_mode_types_1_11_pkg::*; - - ex_code_t ex_src; - logic exception; - - int_code_t intr_src; - logic interrupt, clear_interrupt; - logic interrupt_reg, interrupt_fired, update_mie; - - always_comb begin // determine the source of the interrupt to be stored in the mcause register - interrupt = 1'b1; - intr_src = SOFT_INT_M; - - if (prv_intern_if.ext_int_m) begin - intr_src = EXT_INT_M; - end - else if (prv_intern_if.soft_int_m) begin - intr_src = SOFT_INT_M; - end - else if (prv_intern_if.timer_int_m) begin - intr_src = TIMER_INT_M; - end - else if (prv_intern_if.ext_int_s) begin - intr_src = EXT_INT_S; - end - else if (prv_intern_if.soft_int_s) begin - intr_src = SOFT_INT_S; - end - else if (prv_intern_if.timer_int_s) begin - intr_src = TIMER_INT_S; + import rv32i_types_pkg::*; + import machine_mode_types_1_11_pkg::*; + + ex_code_t ex_src; + logic exception; + + int_code_t intr_src; + logic interrupt, clear_interrupt; + logic interrupt_reg, interrupt_fired, update_mie; + + always_comb begin // determine the source of the interrupt to be stored in the mcause register + interrupt = 1'b1; + intr_src = SOFT_INT_M; + + if (prv_intern_if.ext_int_m) begin + intr_src = EXT_INT_M; + end else if (prv_intern_if.soft_int_m) begin + intr_src = SOFT_INT_M; + end else if (prv_intern_if.timer_int_m) begin + intr_src = TIMER_INT_M; + end else if (prv_intern_if.ext_int_s) begin + intr_src = EXT_INT_S; + end else if (prv_intern_if.soft_int_s) begin + intr_src = SOFT_INT_S; + end else if (prv_intern_if.timer_int_s) begin + intr_src = TIMER_INT_S; + end else if (prv_intern_if.ext_int_u) begin + intr_src = EXT_INT_U; + end else if (prv_intern_if.soft_int_u) begin + intr_src = SOFT_INT_U; + end else if (prv_intern_if.timer_int_u) begin + intr_src = TIMER_INT_U; + end else interrupt = 1'b0; end - else if (prv_intern_if.ext_int_u) begin - intr_src = EXT_INT_U; - end - else if (prv_intern_if.soft_int_u) begin - intr_src = SOFT_INT_U; + + assign clear_interrupt = (prv_intern_if.clear_timer_int_m || prv_intern_if.clear_soft_int_m || prv_intern_if.clear_ext_int_m || prv_intern_if.clear_timer_int_u || prv_intern_if.clear_soft_int_u || prv_intern_if.clear_ext_int_u || prv_intern_if.clear_timer_int_s || prv_intern_if.clear_soft_int_s || prv_intern_if.clear_ext_int_s); + + assign prv_intern_if.mip_rup = interrupt || clear_interrupt; + + always_comb begin // modify the pending status register + prv_intern_if.mip_next = prv_intern_if.mip; + + if (prv_intern_if.ext_int_m) prv_intern_if.mip_next.meip = 1'b1; + else if (prv_intern_if.clear_ext_int_m) prv_intern_if.mip_next.meip = 1'b0; + else if (prv_intern_if.soft_int_m) prv_intern_if.mip_next.msip = 1'b1; + else if (prv_intern_if.clear_soft_int_m) prv_intern_if.mip_next.msip = 1'b0; + else if (prv_intern_if.timer_int_m) prv_intern_if.mip_next.mtip = 1'b1; + else if (prv_intern_if.clear_timer_int_m) prv_intern_if.mip_next.mtip = 1'b0; + else if (prv_intern_if.ext_int_s) prv_intern_if.mip_next.seip = 1'b1; + else if (prv_intern_if.clear_ext_int_s) prv_intern_if.mip_next.seip = 1'b0; + else if (prv_intern_if.soft_int_s) prv_intern_if.mip_next.ssip = 1'b1; + else if (prv_intern_if.clear_soft_int_s) prv_intern_if.mip_next.ssip = 1'b0; + else if (prv_intern_if.timer_int_s) prv_intern_if.mip_next.stip = 1'b1; + else if (prv_intern_if.clear_timer_int_s) prv_intern_if.mip_next.stip = 1'b0; + else if (prv_intern_if.ext_int_u) prv_intern_if.mip_next.ueip = 1'b1; + else if (prv_intern_if.clear_ext_int_u) prv_intern_if.mip_next.ueip = 1'b0; + else if (prv_intern_if.soft_int_u) prv_intern_if.mip_next.usip = 1'b1; + else if (prv_intern_if.clear_soft_int_u) prv_intern_if.mip_next.usip = 1'b0; + else if (prv_intern_if.timer_int_u) prv_intern_if.mip_next.utip = 1'b1; + else if (prv_intern_if.clear_timer_int_u) prv_intern_if.mip_next.utip = 1'b0; + end - else if (prv_intern_if.timer_int_u) begin - intr_src = TIMER_INT_U; + + always_comb begin // determine whether or not an exception occured, as well as the source of the exception + exception = 1'b1; + ex_src = INSN_MAL; + + if (prv_intern_if.breakpoint) ex_src = BREAKPOINT; + else if (prv_intern_if.fault_insn_page) ex_src = INSN_PAGE; + else if (prv_intern_if.fault_insn_access) ex_src = INSN_ACCESS; + else if (prv_intern_if.illegal_insn) ex_src = ILLEGAL_INSN; + else if (prv_intern_if.mal_insn) ex_src = INSN_MAL; + else if (prv_intern_if.env_u) ex_src = ENV_CALL_U; + else if (prv_intern_if.env_s) ex_src = ENV_CALL_S; + else if (prv_intern_if.env_m) ex_src = ENV_CALL_M; + else if (prv_intern_if.mal_s) ex_src = S_ADDR_MAL; + else if (prv_intern_if.mal_l) ex_src = L_ADDR_MAL; + else if (prv_intern_if.fault_store_page) ex_src = STORE_PAGE; + else if (prv_intern_if.fault_load_page) ex_src = LOAD_PAGE; + else if (prv_intern_if.fault_s) ex_src = S_FAULT; + else if (prv_intern_if.fault_l) ex_src = L_FAULT; + else if (prv_intern_if.ex_rmgmt) ex_src = ex_code_t'(prv_intern_if.ex_rmgmt_cause); + else exception = 1'b0; end - else - interrupt = 1'b0; - end - - assign clear_interrupt = (prv_intern_if.clear_timer_int_m || prv_intern_if.clear_soft_int_m || prv_intern_if.clear_ext_int_m || prv_intern_if.clear_timer_int_u || prv_intern_if.clear_soft_int_u || prv_intern_if.clear_ext_int_u || prv_intern_if.clear_timer_int_s || prv_intern_if.clear_soft_int_s || prv_intern_if.clear_ext_int_s); - - assign prv_intern_if.mip_rup = interrupt || clear_interrupt; - - always_comb begin // modify the pending status register - prv_intern_if.mip_next = prv_intern_if.mip; - - if (prv_intern_if.ext_int_m) prv_intern_if.mip_next.meip = 1'b1; - else if (prv_intern_if.clear_ext_int_m) prv_intern_if.mip_next.meip = 1'b0; - else if (prv_intern_if.soft_int_m) prv_intern_if.mip_next.msip = 1'b1; - else if (prv_intern_if.clear_soft_int_m) prv_intern_if.mip_next.msip = 1'b0; - else if (prv_intern_if.timer_int_m) prv_intern_if.mip_next.mtip = 1'b1; - else if (prv_intern_if.clear_timer_int_m) prv_intern_if.mip_next.mtip = 1'b0; - else if (prv_intern_if.ext_int_s) prv_intern_if.mip_next.seip = 1'b1; - else if (prv_intern_if.clear_ext_int_s) prv_intern_if.mip_next.seip = 1'b0; - else if (prv_intern_if.soft_int_s) prv_intern_if.mip_next.ssip = 1'b1; - else if (prv_intern_if.clear_soft_int_s) prv_intern_if.mip_next.ssip = 1'b0; - else if (prv_intern_if.timer_int_s) prv_intern_if.mip_next.stip = 1'b1; - else if (prv_intern_if.clear_timer_int_s) prv_intern_if.mip_next.stip = 1'b0; - else if (prv_intern_if.ext_int_u) prv_intern_if.mip_next.ueip = 1'b1; - else if (prv_intern_if.clear_ext_int_u) prv_intern_if.mip_next.ueip = 1'b0; - else if (prv_intern_if.soft_int_u) prv_intern_if.mip_next.usip = 1'b1; - else if (prv_intern_if.clear_soft_int_u) prv_intern_if.mip_next.usip = 1'b0; - else if (prv_intern_if.timer_int_u) prv_intern_if.mip_next.utip = 1'b1; - else if (prv_intern_if.clear_timer_int_u) prv_intern_if.mip_next.utip = 1'b0; - - end - - always_comb begin // determine whether or not an exception occured, as well as the source of the exception - exception = 1'b1; - ex_src = INSN_MAL; - - if (prv_intern_if.breakpoint) - ex_src = BREAKPOINT; - else if (prv_intern_if.fault_insn_page) - ex_src = INSN_PAGE; - else if (prv_intern_if.fault_insn_access) - ex_src = INSN_ACCESS; - else if (prv_intern_if.illegal_insn) - ex_src = ILLEGAL_INSN; - else if (prv_intern_if.mal_insn) - ex_src = INSN_MAL; - else if (prv_intern_if.env_u) - ex_src = ENV_CALL_U; - else if (prv_intern_if.env_s) - ex_src = ENV_CALL_S; - else if (prv_intern_if.env_m) - ex_src = ENV_CALL_M; - else if (prv_intern_if.mal_s) - ex_src = S_ADDR_MAL; - else if (prv_intern_if.mal_l) - ex_src = L_ADDR_MAL; - else if (prv_intern_if.fault_store_page) - ex_src = STORE_PAGE; - else if (prv_intern_if.fault_load_page) - ex_src = LOAD_PAGE; - else if (prv_intern_if.fault_s) - ex_src = S_FAULT; - else if (prv_intern_if.fault_l) - ex_src = L_FAULT; - else if (prv_intern_if.ex_rmgmt) - ex_src = ex_code_t'(prv_intern_if.ex_rmgmt_cause); - else - exception = 1'b0; - end - - //output to pipeline control - assign prv_intern_if.intr = exception | interrupt_reg; - assign interrupt_fired = (prv_intern_if.mstatus.mie & ((prv_intern_if.mie.mtie & prv_intern_if.mip.mtip) | + //output to pipeline control + assign prv_intern_if.intr = exception | interrupt_reg; + assign interrupt_fired = (prv_intern_if.mstatus.mie & ((prv_intern_if.mie.mtie & prv_intern_if.mip.mtip) | (prv_intern_if.mie.msie & prv_intern_if.mip.msip) | (prv_intern_if.mie.meie & prv_intern_if.mip.meip))); - - // Register Updates on Interrupt/Exception - assign prv_intern_if.mcause_rup = exception | interrupt; // TODO: Change to interrupt - assign prv_intern_if.mcause_next.interrupt = ~exception; - assign prv_intern_if.mcause_next.cause = exception ? ex_src : intr_src; - - assign prv_intern_if.mstatus_rup = exception | prv_intern_if.intr | prv_intern_if.mret; // TODO: Change to prv_intern_if.intr - - always_comb begin - prv_intern_if.mstatus_next.mie = prv_intern_if.mstatus.mie; - prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mpie; - //changed from intr - if (update_mie) begin // interrupt has truly been registered and it is time to go to the vector table - prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mie; // when a trap is taken mpie is set to the current mie - prv_intern_if.mstatus_next.mie = 1'b0; // disable the interrupt once it enters the handler - - end else if (prv_intern_if.mret) begin // leaving the vector table - prv_intern_if.mstatus_next.mpie = 1'b1; - prv_intern_if.mstatus_next.mie = prv_intern_if.mstatus.mpie; + + // Register Updates on Interrupt/Exception + assign prv_intern_if.mcause_rup = exception | interrupt; // TODO: Change to interrupt + assign prv_intern_if.mcause_next.interrupt = ~exception; + assign prv_intern_if.mcause_next.cause = exception ? ex_src : intr_src; + + assign prv_intern_if.mstatus_rup = exception | prv_intern_if.intr | prv_intern_if.mret; // TODO: Change to prv_intern_if.intr + + always_comb begin + prv_intern_if.mstatus_next.mie = prv_intern_if.mstatus.mie; + prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mpie; + //changed from intr + if (update_mie) begin // interrupt has truly been registered and it is time to go to the vector table + prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mie; // when a trap is taken mpie is set to the current mie + prv_intern_if.mstatus_next.mie = 1'b0; // disable the interrupt once it enters the handler + + end else if (prv_intern_if.mret) begin // leaving the vector table + prv_intern_if.mstatus_next.mpie = 1'b1; + prv_intern_if.mstatus_next.mie = prv_intern_if.mstatus.mpie; + end end - end - // Update EPC as soon as interrupt or exception is found - // Note: mepc cannot update immediately, as if the processor is in an interrupt already, - // the MEPC captured will be within the interrupt (and nested interrupts are not supported). - // Interrupt fired notes when an interrupt is seen by the processor, i.e. when mstatus.mie is high again. - // The signal is 2 cycles long, so the update_mie signal is used to clip it down to 1 to prevent MEPC - // double update which results in skipping an instruction. - assign prv_intern_if.mepc_rup = exception | (interrupt_fired & ~update_mie); // TODO: Change to interrupt - assign prv_intern_if.mepc_next = prv_intern_if.epc; + // Update EPC as soon as interrupt or exception is found + // Note: mepc cannot update immediately, as if the processor is in an interrupt already, + // the MEPC captured will be within the interrupt (and nested interrupts are not supported). + // Interrupt fired notes when an interrupt is seen by the processor, i.e. when mstatus.mie is high again. + // The signal is 2 cycles long, so the update_mie signal is used to clip it down to 1 to prevent MEPC + // double update which results in skipping an instruction. + assign prv_intern_if.mepc_rup = exception | (interrupt_fired & ~update_mie); // TODO: Change to interrupt + assign prv_intern_if.mepc_next = prv_intern_if.epc; - assign prv_intern_if.mtval_rup = (prv_intern_if.mal_l | prv_intern_if.fault_l | prv_intern_if.mal_s | prv_intern_if.fault_s | - prv_intern_if.illegal_insn | prv_intern_if.fault_insn_access | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) + assign prv_intern_if.mtval_rup = (prv_intern_if.mal_l | prv_intern_if.fault_l | prv_intern_if.mal_s | prv_intern_if.fault_s | + prv_intern_if.illegal_insn | prv_intern_if.fault_insn_access | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) & prv_intern_if.pipe_clear; // TODO: May need to insert other exception signals - assign prv_intern_if.mtval_next = prv_intern_if.mtval; - - /* Interrupt needs to be latched until pipeline cleared */ - /* because mstatus.ie causes the irq to disappear after */ - /* one cycle. Cannot wait to clear mstatus.ie because */ - /* then another interrupt can fire during pipeline clear */ - always_ff @ (posedge CLK, negedge nRST) begin - if (!nRST) - interrupt_reg <= '0; - else if (interrupt_fired) - interrupt_reg <= 1'b1; - else if (prv_intern_if.pipe_clear) - interrupt_reg <= '0; - end - - /* + assign prv_intern_if.mtval_next = prv_intern_if.mtval; + + /* Interrupt needs to be latched until pipeline cleared */ + /* because mstatus.ie causes the irq to disappear after */ + /* one cycle. Cannot wait to clear mstatus.ie because */ + /* then another interrupt can fire during pipeline clear */ + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) interrupt_reg <= '0; + else if (interrupt_fired) interrupt_reg <= 1'b1; + else if (prv_intern_if.pipe_clear) interrupt_reg <= '0; + end + + /* * Fix for MIE/MPIE issue. This used to be the same as 'interrupt_reg' above, * but the above stays high for 2+ cycles (i.e. waiting for pipe_clear). * This caused MPIE to update twice; the first update would set MPIE to 1, @@ -205,15 +176,11 @@ module priv_1_11_control ( * extra register was the cleanest solution to ensuring MPIE updates exactly * once. */ - always_ff @(posedge CLK, negedge nRST) begin - if(!nRST) - update_mie <= '0; - else if(interrupt_fired && ~update_mie) - update_mie <= 1'b1; - else if (prv_intern_if.pipe_clear) - update_mie <= '0; - else - update_mie <= '0; - end + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) update_mie <= '0; + else if (interrupt_fired && ~update_mie) update_mie <= 1'b1; + else if (prv_intern_if.pipe_clear) update_mie <= '0; + else update_mie <= '0; + end endmodule diff --git a/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv b/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv index 127558fc8..35c9bd86c 100644 --- a/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv +++ b/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -32,7 +32,7 @@ module priv_1_11_csr_rfile ( ); import machine_mode_types_1_11_pkg::*; import rv32i_types_pkg::*; - + /* Machine Information Registers */ mvendorid_t mvendorid; @@ -45,7 +45,7 @@ module priv_1_11_csr_rfile ( assign misaid_default.zero = '0; assign misaid_default.extensions = MISAID_EXT_I `ifdef RV32M_SUPPORTED | MISAID_EXT_M `endif `ifdef RV32C_SUPPORTED | - MISAID_EXT_C `endif `ifdef RV32F_SUPPORTED | + MISAID_EXT_C `endif `ifdef RV32F_SUPPORTED | MISAID_EXT_F `endif `ifdef CUSTOM_SUPPORTED | MISAID_EXT_X `endif; @@ -53,9 +53,9 @@ module priv_1_11_csr_rfile ( assign mvendorid = '0; assign marchid = '0; assign mimpid = '0; - assign mhartid = '0; + assign mhartid = '0; + - /* Machine Trap Setup Registers */ mstatus_t mstatus, mstatus_next; @@ -66,12 +66,12 @@ module priv_1_11_csr_rfile ( /* // Privilege and Global Interrupt-Enable Stack assign mstatus_next.uie = 1'b0; - assign mstatus_next.sie = 1'b0; + assign mstatus_next.sie = 1'b0; assign mstatus_next.reserved_0 = 1'b0; - assign mstatus_next.upie = 1'b0; - assign mstatus_next.spie = 1'b0; + assign mstatus_next.upie = 1'b0; + assign mstatus_next.spie = 1'b0; assign mstatus_next.reserved_1 = 1'b0; - assign mstatus_next.spp = 1'b0; + assign mstatus_next.spp = 1'b0; assign mstatus_next.reserved_2 = 2'b0; assign mstatus_next.mpp = M_LEVEL; @@ -110,7 +110,7 @@ module priv_1_11_csr_rfile ( assign mie_next.seie = 1'b0; */ /* Machine Trap Handling */ - + mscratch_t mscratch, mscratch_next; mepc_t mepc, mepc_next; mcause_t mcause, mcause_next; @@ -149,7 +149,7 @@ module priv_1_11_csr_rfile ( assign instretfull_next = (prv_intern_if.instr_retired == 1'b1) ? instretfull + 1 : instretfull; - + always_ff @ (posedge CLK, negedge nRST) begin if (~nRST) begin mstatus <= '0; @@ -169,7 +169,7 @@ module priv_1_11_csr_rfile ( timefull <= '0; cyclefull <= '0; instretfull <= '0; - end else begin + end else begin mstatus <= mstatus_next; //mstatus.mie <= mstatus_next.mie; //mstatus.mpie <= mstatus_next.mpie; @@ -225,7 +225,7 @@ module priv_1_11_csr_rfile ( mstatus ); assign mepc_next = (prv_intern_if.addr == MEPC_ADDR) ? mepc_t'(rup_data) : ( - prv_intern_if.mepc_rup ? prv_intern_if.mepc_next : + prv_intern_if.mepc_rup ? prv_intern_if.mepc_next : mepc ); @@ -237,7 +237,7 @@ module priv_1_11_csr_rfile ( // Ensure legal MISA value - WARL always_comb begin misaid_temp = misaid_t'(rup_data); - if(prv_intern_if.addr == MISA_ADDR && misaid_temp.base != 2'b00 + if(prv_intern_if.addr == MISA_ADDR && misaid_temp.base != 2'b00 && (misaid_temp.extensions & MISAID_EXT_E) ^ (misaid_temp.extensions & MISAID_EXT_I) != 'b1 && misaid_temp.zero == 4'b0) begin misaid_next = misaid_temp; @@ -253,19 +253,19 @@ module priv_1_11_csr_rfile ( MARCHID_ADDR : prv_intern_if.rdata = marchid; MIMPID_ADDR : prv_intern_if.rdata = mimpid; MHARTID_ADDR : prv_intern_if.rdata = mhartid; - MISA_ADDR : prv_intern_if.rdata = misaid; + MISA_ADDR : prv_intern_if.rdata = misaid; MSTATUS_ADDR : prv_intern_if.rdata = mstatus; MTVEC_ADDR : prv_intern_if.rdata = mtvec; - MEDELEG_ADDR : prv_intern_if.rdata = medeleg; - MIDELEG_ADDR : prv_intern_if.rdata = mideleg; + MEDELEG_ADDR : prv_intern_if.rdata = medeleg; + MIDELEG_ADDR : prv_intern_if.rdata = mideleg; MIE_ADDR : prv_intern_if.rdata = mie; MSCRATCH_ADDR : prv_intern_if.rdata = mscratch; MEPC_ADDR : prv_intern_if.rdata = mepc; MCAUSE_ADDR : prv_intern_if.rdata = mcause; MTVAL_ADDR : prv_intern_if.rdata = mtval; - MIP_ADDR : prv_intern_if.rdata = mip; + MIP_ADDR : prv_intern_if.rdata = mip; // Performance counters MCYCLE_ADDR : prv_intern_if.rdata = cycle; diff --git a/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv b/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv index f46a6e082..03938b373 100644 --- a/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv +++ b/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,34 +20,32 @@ * Email: steven69@purdue.edu * Date Created: 08/13/2019 * Description: Control signals for the pipeline from the exception/IRQ -* block +* block */ // Code will mainly be used as pipeline control `include "priv_1_11_internal_if.vh" -module priv_1_11_pipeline_control -( - priv_1_11_internal_if.pipe_ctrl prv_intern_if // interface for pipeline control +module priv_1_11_pipeline_control ( + priv_1_11_internal_if.pipe_ctrl prv_intern_if // interface for pipeline control ); - import machine_mode_types_1_11_pkg::*; - import rv32i_types_pkg::*; - - assign prv_intern_if.insert_pc = prv_intern_if.mret | (prv_intern_if.pipe_clear & prv_intern_if.intr); // insert the PC - - - always_comb begin - prv_intern_if.priv_pc = '0; - - if(prv_intern_if.intr) begin - if (prv_intern_if.mtvec.mode == VECTORED & prv_intern_if.mcause.interrupt) begin // vectored mode based on the interrupt source - prv_intern_if.priv_pc = (prv_intern_if.mtvec.base << 2) + (prv_intern_if.mcause.cause << 2); - end else - prv_intern_if.priv_pc = prv_intern_if.mtvec.base << 2; - - end else if (prv_intern_if.mret) - prv_intern_if.priv_pc = prv_intern_if.mepc; // when leaving the ISR, restore to the original PC - - end + import machine_mode_types_1_11_pkg::*; + import rv32i_types_pkg::*; + + assign prv_intern_if.insert_pc = prv_intern_if.mret | (prv_intern_if.pipe_clear & prv_intern_if.intr); // insert the PC + + + always_comb begin + prv_intern_if.priv_pc = '0; + + if (prv_intern_if.intr) begin + if (prv_intern_if.mtvec.mode == VECTORED & prv_intern_if.mcause.interrupt) begin // vectored mode based on the interrupt source + prv_intern_if.priv_pc = {prv_intern_if.mtvec.base, 2'b00} + {prv_intern_if.mcause.cause, 2'b00}; + end else prv_intern_if.priv_pc = prv_intern_if.mtvec.base << 2; + + end else if (prv_intern_if.mret) + prv_intern_if.priv_pc = prv_intern_if.mepc; // when leaving the ISR, restore to the original PC + + end endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_block.sv b/source_code/privs/priv_1_12/priv_1_12_block.sv index 63ffeb2e3..7fac95f94 100644 --- a/source_code/privs/priv_1_12/priv_1_12_block.sv +++ b/source_code/privs/priv_1_12/priv_1_12_block.sv @@ -88,8 +88,8 @@ module priv_1_12_block ( assign prv_intern_if.mal_s = prv_pipe_if.mal_s; assign prv_intern_if.breakpoint = prv_pipe_if.breakpoint; assign prv_intern_if.env_m = prv_pipe_if.env_m; - assign prv_intern_if.env_s = 1'b0; - assign prv_intern_if.env_u = 1'b0; + assign prv_intern_if.env_s = 1'b0; + assign prv_intern_if.env_u = 1'b0; assign prv_intern_if.fault_insn_page = 1'b0; assign prv_intern_if.fault_load_page = 1'b0; assign prv_intern_if.fault_store_page = 1'b0; diff --git a/source_code/privs/priv_1_12/priv_1_12_csr.sv b/source_code/privs/priv_1_12/priv_1_12_csr.sv index 853ae390e..614eb84a4 100644 --- a/source_code/privs/priv_1_12/priv_1_12_csr.sv +++ b/source_code/privs/priv_1_12/priv_1_12_csr.sv @@ -26,9 +26,11 @@ `include "priv_ext_if.vh" `include "component_selection_defines.vh" -module priv_1_12_csr # ( - HARTID = 0 ) ( - input CLK, nRST, +module priv_1_12_csr #( + parameter int HARTID = 0 +)( + input CLK, + input nRST, priv_1_12_internal_if.csr prv_intern_if `ifdef RV32F_SUPPORTED , priv_ext_if.priv priv_ext_f_if @@ -77,16 +79,20 @@ module priv_1_12_csr # ( assign prv_intern_if.invalid_csr = invalid_csr_0 | invalid_csr_1; // Extension Broadcast Signals - `ifdef RV32F_SUPPORTED +`ifdef RV32F_SUPPORTED assign priv_ext_f_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_f_if.value_in = nxt_csr_val; - assign priv_ext_f_if.csr_active = prv_intern_if.valid_write & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); - `endif // RV32F_SUPPORTED - `ifdef RV32V_SUPPORTED + assign priv_ext_f_if.csr_active = prv_intern_if.valid_write + & (prv_intern_if.csr_write | prv_intern_if.csr_set + | prv_intern_if.csr_clear); +`endif // RV32F_SUPPORTED +`ifdef RV32V_SUPPORTED assign priv_ext_v_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_v_if.value_in = nxt_csr_val; - assign priv_ext_v_if.csr_active = prv_intern_if.valid_write & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); - `endif // RV32V_SUPPORTED + assign priv_ext_v_if.csr_active = prv_intern_if.valid_write + & (prv_intern_if.csr_write | prv_intern_if.csr_set + | prv_intern_if.csr_clear); +`endif // RV32V_SUPPORTED /* Save some logic with this */ assign mcycle = cycles_full[31:0]; @@ -224,11 +230,11 @@ module priv_1_12_csr # ( mtval_next = mtval; mcounteren_next = mcounteren; mcounterinhibit_next = mcounterinhibit; - mcause_next = mcause_next; + mcause_next = mcause; nxt_csr_val = (prv_intern_if.csr_write) ? prv_intern_if.new_csr_val : (prv_intern_if.csr_set) ? prv_intern_if.new_csr_val | prv_intern_if.old_csr_val : - (prv_intern_if.csr_clear) ? ~prv_intern_if.new_csr_val & prv_intern_if.old_csr_val : + (prv_intern_if.csr_clear) ? ~prv_intern_if.new_csr_val & prv_intern_if.old_csr_val : prv_intern_if.new_csr_val; invalid_csr_0 = 1'b0; @@ -296,19 +302,19 @@ module priv_1_12_csr # ( // inject values if (prv_intern_if.inject_mstatus) begin mstatus_next = prv_intern_if.next_mstatus; - end + end if (prv_intern_if.inject_mtval) begin mtval_next = prv_intern_if.next_mtval; - end + end if (prv_intern_if.inject_mepc) begin mepc_next = prv_intern_if.next_mepc; - end + end if (prv_intern_if.inject_mcause) begin mcause_next = prv_intern_if.next_mcause; - end + end if (prv_intern_if.inject_mie) begin mie_next = prv_intern_if.next_mie; - end + end if (prv_intern_if.inject_mip) begin mip_next = prv_intern_if.next_mip; end @@ -366,7 +372,7 @@ module priv_1_12_csr # ( prv_intern_if.old_csr_val = priv_ext_v_if.value_out; end `endif // RV32V_SUPPORTED - + // CSR address doesn't exist invalid_csr_1 = 1'b1 `ifdef RV32F_SUPPORTED diff --git a/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv index f21420a75..622fb3bca 100644 --- a/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv +++ b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv @@ -68,7 +68,9 @@ module priv_1_12_int_ex_handler ( end end - assign clear_interrupt = (prv_intern_if.clear_timer_int_m | prv_intern_if.clear_soft_int_m | prv_intern_if.clear_ext_int_m | prv_intern_if.clear_timer_int_s | prv_intern_if.clear_soft_int_s | prv_intern_if.clear_ext_int_s); + assign clear_interrupt = (prv_intern_if.clear_timer_int_m | prv_intern_if.clear_soft_int_m + | prv_intern_if.clear_ext_int_m | prv_intern_if.clear_timer_int_s + | prv_intern_if.clear_soft_int_s | prv_intern_if.clear_ext_int_s); // Determine whether an exception occured always_comb begin @@ -111,8 +113,10 @@ module priv_1_12_int_ex_handler ( // Output info to pipe_ctrl assign prv_intern_if.intr = exception | interrupt_reg; - assign interrupt_fired = (prv_intern_if.curr_mstatus.mie & ((prv_intern_if.curr_mie.mtie & prv_intern_if.curr_mip.mtip) | - (prv_intern_if.curr_mie.msie & prv_intern_if.curr_mip.msip) | (prv_intern_if.curr_mie.meie & prv_intern_if.curr_mip.meip))); + assign interrupt_fired = (prv_intern_if.curr_mstatus.mie & + ((prv_intern_if.curr_mie.mtie & prv_intern_if.curr_mip.mtip) + | (prv_intern_if.curr_mie.msie & prv_intern_if.curr_mip.msip) + | (prv_intern_if.curr_mie.meie & prv_intern_if.curr_mip.meip))); // Register updates on Interrupts/Exceptions /* NOTE a lot of the below code are patterns that exist to solve issues @@ -145,13 +149,16 @@ module priv_1_12_int_ex_handler ( else if (prv_intern_if.clear_timer_int_s) prv_intern_if.next_mip.stip = 1'b0; end - assign prv_intern_if.inject_mstatus = exception | prv_intern_if.intr | prv_intern_if.mret | prv_intern_if.sret | prv_intern_if.uret; + assign prv_intern_if.inject_mstatus = exception | prv_intern_if.intr | prv_intern_if.mret + | prv_intern_if.sret | prv_intern_if.uret; always_comb begin prv_intern_if.next_mstatus.mie = prv_intern_if.curr_mstatus.mie; prv_intern_if.next_mstatus.mpie = prv_intern_if.curr_mstatus.mpie; - if (update_mie) begin // interrupt has truly been registered and it is time to go to the vector table - prv_intern_if.next_mstatus.mpie = prv_intern_if.curr_mstatus.mie; // when a trap is taken mpie is set to the current mie + // interrupt has truly been registered and it is time to go to the vector table + if (update_mie) begin + // when a trap is taken mpie is set to the current mie + prv_intern_if.next_mstatus.mpie = prv_intern_if.curr_mstatus.mie; prv_intern_if.next_mstatus.mie = 1'b0; end else if (prv_intern_if.mret) begin prv_intern_if.next_mstatus.mpie = 1'b0; // leaving the vector table @@ -165,12 +172,17 @@ module priv_1_12_int_ex_handler ( // Interrupt fired notes when an interrupt is seen by the processor, i.e. when mstatus.mie is high again. // The signal is 2 cycles long, so the update_mie signal is used to clip it down to 1 to prevent MEPC // double update which results in skipping an instruction. - assign prv_intern_if.inject_mepc = exception | (interrupt_fired & ~update_mie); // TODO: Change to interrupt + // TODO: Change to interrupt + assign prv_intern_if.inject_mepc = exception | (interrupt_fired & ~update_mie); assign prv_intern_if.next_mepc = prv_intern_if.epc; - assign prv_intern_if.inject_mtval = (prv_intern_if.mal_l | prv_intern_if.fault_l | prv_intern_if.mal_s | prv_intern_if.fault_s | - prv_intern_if.illegal_insn | prv_intern_if.fault_insn_access | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) - & prv_intern_if.pipe_clear; // TODO: May need to insert other exception signals + // TODO: May need to insert other exception signals + assign prv_intern_if.inject_mtval = (prv_intern_if.mal_l | prv_intern_if.fault_l + | prv_intern_if.mal_s | prv_intern_if.fault_s + | prv_intern_if.illegal_insn + | prv_intern_if.fault_insn_access + | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) + & prv_intern_if.pipe_clear; assign prv_intern_if.next_mtval = prv_intern_if.curr_mtval; /* Interrupt needs to be latched until pipeline cleared */ diff --git a/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv b/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv index 218be39f3..33b7d0e4b 100644 --- a/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv +++ b/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv @@ -39,12 +39,15 @@ module priv_1_12_pipe_control ( prv_intern_if.priv_pc = '0; if (prv_intern_if.intr) begin - if (prv_intern_if.curr_mtvec.mode == VECTORED & prv_intern_if.curr_mcause.interrupt) begin // vectored mode based on the interrupt source - prv_intern_if.priv_pc = (prv_intern_if.curr_mtvec.base << 2) + (prv_intern_if.curr_mcause.cause << 2); - end else + if (prv_intern_if.curr_mtvec.mode == VECTORED & prv_intern_if.curr_mcause.interrupt) begin + prv_intern_if.priv_pc = (prv_intern_if.curr_mtvec.base << 2) + + (prv_intern_if.curr_mcause.cause << 2); + end else begin prv_intern_if.priv_pc = prv_intern_if.curr_mtvec.base << 2; - end else if (prv_intern_if.mret) - prv_intern_if.priv_pc = prv_intern_if.curr_mepc; // when leaving the ISR, restore to the original PC + end + end else if (prv_intern_if.mret) begin + prv_intern_if.priv_pc = prv_intern_if.curr_mepc; // Leaving ISR + end end endmodule diff --git a/source_code/privs/priv_wrapper.sv b/source_code/privs/priv_wrapper.sv index bd46c2022..ddda7d1e6 100644 --- a/source_code/privs/priv_wrapper.sv +++ b/source_code/privs/priv_wrapper.sv @@ -26,11 +26,12 @@ `include "core_interrupt_if.vh" module priv_wrapper ( - input logic CLK, nRST, - prv_pipeline_if.priv_block prv_pipe_if, - core_interrupt_if.core interrupt_if + input logic CLK, + nRST, + prv_pipeline_if.priv_block prv_pipe_if, + core_interrupt_if.core interrupt_if ); - priv_1_12_block priv_block_i(.*); + priv_1_12_block priv_block_i(.*); endmodule diff --git a/source_code/privs/wscript b/source_code/privs/wscript deleted file mode 100644 index 99e992732..000000000 --- a/source_code/privs/wscript +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(priv_1_11) - -def sim_source(cnf): - cnf.recurse(priv_1_11) diff --git a/source_code/ram/config_ram_wrapper.sv b/source_code/ram/config_ram_wrapper.sv index 6720d92de..6cd3f2fbc 100644 --- a/source_code/ram/config_ram_wrapper.sv +++ b/source_code/ram/config_ram_wrapper.sv @@ -1,62 +1,65 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: config_ram_wrapper.sv -* -* Created by: John Skubic +* +* Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 -* Description: Config Ram wrapper should contain the ram module provided by the -* simulation environment being used. If no ram modules are -* provided, an emulated ram module must be created. +* Description: Config Ram wrapper should contain the ram module provided by the +* simulation environment being used. If no ram modules are +* provided, an emulated ram module must be created. * Config ram differs from ram wrapper in that it is not restricted -* to the word size of the core. For that reason, no interface is +* to the word size of the core. For that reason, no interface is * used for config_ram_wrapper to preserve its flexibility. */ module config_ram_wrapper #( - parameter N_BYTES = 4, - parameter DEPTH = 256, - parameter LAT = 0, - parameter ADDR_BITS = $clog2(DEPTH), - parameter N_BITS = N_BYTES*8 + parameter N_BYTES = 4, + parameter DEPTH = 256, + parameter LAT = 0, + parameter ADDR_BITS = $clog2(DEPTH), + parameter N_BITS = N_BYTES * 8 ) ( - input logic CLK, nRST, - input logic [N_BITS-1:0] wdata, - input logic [ADDR_BITS-1:0] addr, - input logic [N_BYTES-1:0] byte_en, - input logic wen, ren, - output logic [N_BITS-1:0] rdata, - output logic busy + input logic CLK, + nRST, + input logic [N_BITS-1:0] wdata, + input logic [ADDR_BITS-1:0] addr, + input logic [N_BYTES-1:0] byte_en, + input logic wen, + ren, + output logic [N_BITS-1:0] rdata, + output logic busy ); - ram_sim_model #(.LAT(0), - .ENDIANNESS("little"), - .N_BYTES(N_BYTES), - .DEPTH(DEPTH) - ) v_lat_ram ( - .CLK(CLK), - .nRST(nRST), - .wdata_in(wdata), - .addr_in(addr), - .byte_en_in(byte_en), - .wen_in(wen), - .ren_in(ren), - .rdata_out(rdata), - .busy_out(busy) - ); + ram_sim_model #( + .LAT(0), + .ENDIANNESS("little"), + .N_BYTES(N_BYTES), + .DEPTH(DEPTH) + ) v_lat_ram ( + .CLK(CLK), + .nRST(nRST), + .wdata_in(wdata), + .addr_in(addr), + .byte_en_in(byte_en), + .wen_in(wen), + .ren_in(ren), + .rdata_out(rdata), + .busy_out(busy) + ); endmodule diff --git a/source_code/ram/ram_sim_model.sv b/source_code/ram/ram_sim_model.sv index 91089e4c8..3b644fae4 100644 --- a/source_code/ram/ram_sim_model.sv +++ b/source_code/ram/ram_sim_model.sv @@ -1,175 +1,199 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: ram_sim_model.sv -* -* Created by: John Skubic +* +* Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 04/27/2017 * Description: SRAM model. This is NOT SYNTHESIZABLE but allows this -* project to run independent of tools in simulation. +* project to run independent of tools in simulation. */ module ram_sim_model #( - parameter LAT = 0, // ram latency - parameter ENDIANNESS = "little", - parameter N_BYTES = 4, - parameter DEPTH = 8192, - parameter MEM_INIT_FILE = "meminit.hex", - parameter MEM_DEFAULT = 32'h0000_0000, - parameter ADDR_BITS = $clog2(DEPTH), - parameter COUNT_BITS = $clog2(LAT) + 1, - parameter N_BITS = N_BYTES*8 + parameter LAT = 0, // ram latency + parameter ENDIANNESS = "little", + parameter N_BYTES = 4, + parameter DEPTH = 8192, + parameter MEM_INIT_FILE = "meminit.hex", + parameter MEM_DEFAULT = 32'h0000_0000, + parameter ADDR_BITS = $clog2(DEPTH), + parameter COUNT_BITS = $clog2(LAT) + 1, + parameter N_BITS = N_BYTES * 8 ) ( - input logic CLK, nRST, - input logic [N_BITS-1:0] wdata_in, - input logic [ADDR_BITS-1:0] addr_in, - input logic [N_BYTES-1:0] byte_en_in, - input logic wen_in, ren_in, - output logic [N_BITS-1:0] rdata_out, - output logic busy_out + input logic CLK, + nRST, + input logic [N_BITS-1:0] wdata_in, + input logic [ADDR_BITS-1:0] addr_in, + input logic [N_BYTES-1:0] byte_en_in, + input logic wen_in, + ren_in, + output logic [N_BITS-1:0] rdata_out, + output logic busy_out ); - - // Memory as associative array to try and save space at runtime - logic [N_BITS-1:0] memory [*]; - - // Variables for file IO - integer fptr; - logic [ADDR_BITS-1:0] faddr; - logic [7:0] line_type; - logic [31:0] fdata; - logic [1:0] t0, t1; - - // RAM signals - logic [COUNT_BITS-1:0] counter; - logic [ADDR_BITS-1:0] addr, addr_r, addr_ram; - logic ren, ren_r, ren_ram; - logic wen, wen_r, wen_ram; - logic [N_BYTES-1:0] byte_en; - logic [N_BITS-1:0] rdata, wdata, wdata_r, wdata_ram, mask; - logic input_diff; - logic access; - string line; - int res; - - genvar i; - - // Load in meminit - initial begin - if (MEM_INIT_FILE != "") begin - fptr = $fopen(MEM_INIT_FILE, "r"); - if (!fptr) begin - $info("Warning: Couldn't open memory init file %s", MEM_INIT_FILE); - end else begin - while (!$feof(fptr)) begin - res = $fgets(line, fptr); - res = $sscanf(line, ":%2h%4h%2h%8h%2h", t0, faddr, line_type, fdata, t1); - if (line_type == 8'h00) begin //data - memory[faddr] = fdata; - //$info("Loading %0h into addr %0h", fdata, faddr); - end + genvar i; + // Memory as associative array to try and save space at runtime +`ifndef SYNTHESIS + logic [N_BITS-1:0] memory [*]; +`else + logic [N_BITS-1:0] memory [DEPTH-1:0]; +`endif + // Variables for file IO + integer fptr; + logic [ADDR_BITS-1:0] faddr; + logic [7:0] line_type; + logic [31:0] fdata; + logic [1:0] t0, t1; + + // RAM signals + logic [COUNT_BITS-1:0] counter; + logic [ADDR_BITS-1:0] addr, addr_r, addr_ram; + logic ren, ren_r, ren_ram; + logic wen, wen_r, wen_ram; + logic [N_BYTES-1:0] byte_en; + logic [N_BITS-1:0] rdata, wdata, wdata_r, wdata_ram, mask; + logic input_diff; + logic access; + string line; + int res; + + + // Load in meminit + initial begin + if (MEM_INIT_FILE != "") begin + fptr = $fopen(MEM_INIT_FILE, "r"); + if (!fptr) begin + $info("Warning: Couldn't open memory init file %s", MEM_INIT_FILE); + end else begin + while (!$feof( + fptr + )) begin + res = $fgets(line, fptr); + res = $sscanf(line, ":%2h%4h%2h%8h%2h", t0, faddr, line_type, fdata, t1); + if (line_type == 8'h00) begin //data + memory[faddr] = fdata; + //$info("Loading %0h into addr %0h", fdata, faddr); + end + end + $fclose(fptr); + end end - $fclose(fptr); - end end - end - /* + /* * * Begin steady state ram functionality * */ - // Changes for bus endianness - generate - if (ENDIANNESS == "big") begin - // swap endianness - endian_swapper # (.N_BYTES(N_BYTES)) write_swap(.word_in(wdata_in), .word_out(wdata)); - endian_swapper # (.N_BYTES(N_BYTES)) read_swap(.word_in(rdata), .word_out(rdata_out)); - - // swap byte enables - for (i=0; i < N_BYTES/2; i++) begin - assign byte_en[N_BYTES-1-i] = byte_en_in[i]; - assign byte_en[i] = byte_en_in[N_BYTES-1-i]; - end - if (N_BYTES%2) - assign byte_en[N_BYTES/2] = byte_en_in[N_BYTES/2]; - - end else if (ENDIANNESS == "little") begin - assign wdata = wdata_in; - assign byte_en = byte_en_in; - assign rdata_out = rdata; + // Changes for bus endianness + generate + if (ENDIANNESS == "big") begin : g_ram_be + // swap endianness + endian_swapper #( + .N_BYTES(N_BYTES) + ) write_swap ( + .word_in (wdata_in), + .word_out(wdata) + ); + endian_swapper #( + .N_BYTES(N_BYTES) + ) read_swap ( + .word_in (rdata), + .word_out(rdata_out) + ); + + // swap byte enables + for (i = 0; i < N_BYTES / 2; i++) begin : g_ram_be_byte_enable + assign byte_en[N_BYTES-1-i] = byte_en_in[i]; + assign byte_en[i] = byte_en_in[N_BYTES-1-i]; + end + if (N_BYTES % 2) assign byte_en[N_BYTES/2] = byte_en_in[N_BYTES/2]; + + end else if (ENDIANNESS == "little") begin : g_ram_le + assign wdata = wdata_in; + assign byte_en = byte_en_in; + assign rdata_out = rdata; + end + endgenerate + + generate + for (i = 0; i < N_BYTES; i++) begin : g_ram_mask + assign mask[i*8+:8] = {8{byte_en[i]}}; + end + endgenerate + +`ifndef SYNTHESIS + always begin + @(posedge CLK); + if (access && wen_ram) begin + if (memory.exists(addr_ram)) + memory[addr_ram] = (wdata_ram & mask) | (memory[addr_ram] & ~mask); + else memory[addr_ram] = wdata_ram & mask; + end end - endgenerate - - generate - for(i=0; i < N_BYTES; i++) - assign mask[i*8+:8] = {8{byte_en[i]}}; - endgenerate - - always begin - @(posedge CLK); - if (access && wen_ram) begin - if (memory.exists(addr_ram)) - memory[addr_ram] = (wdata_ram & mask) | (memory[addr_ram] & ~mask); - else - memory[addr_ram] = wdata_ram & mask; +`else + always_ff @(posedge CLK) begin + if (access && wen_ram) begin + memory[addr_ram] <= (wdata_ram & mask) | (memory[addr_ram] & ~mask); + end end - end - - assign addr = addr_in[ADDR_BITS-1:0]; - assign ren = ren_in; - assign wen = wen_in; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) begin - addr_r <= '0; - ren_r <= '0; - wen_r <= '0; - wdata_r <= '0; - end else begin - addr_r <= addr; - ren_r <= ren; - wen_r <= wen; - wdata_r <= wdata; +`endif + + assign addr = addr_in[ADDR_BITS-1:0]; + assign ren = ren_in; + assign wen = wen_in; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + addr_r <= '0; + ren_r <= '0; + wen_r <= '0; + wdata_r <= '0; + end else begin + addr_r <= addr; + ren_r <= ren; + wen_r <= wen; + wdata_r <= wdata; + end end - end - - assign input_diff = (addr_r !== addr) || (ren_r !== ren) || + + assign input_diff = (addr_r !== addr) || (ren_r !== ren) || (wen_r !== wen) || (wdata_r !== wdata); - // mux inputs to ram - assign addr_ram = input_diff ? addr : addr_r; - assign ren_ram = input_diff ? ren : ren_r; - assign wen_ram = input_diff ? wen : wen_r; - assign wdata_ram = input_diff ? wdata: wdata_r; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - counter <= '0; - else if (input_diff) - counter <= '0; - if (counter != LAT) - counter <= counter + 1; - end - - assign access = (counter == LAT) && !input_diff; - - assign rdata = (^addr_ram !== 1'bx) && memory.exists(addr_ram) ? memory[addr_ram] : MEM_DEFAULT; - assign busy_out = ~access; + // mux inputs to ram + assign addr_ram = input_diff ? addr : addr_r; + assign ren_ram = input_diff ? ren : ren_r; + assign wen_ram = input_diff ? wen : wen_r; + assign wdata_ram = input_diff ? wdata : wdata_r; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) counter <= '0; + else if (input_diff) counter <= '0; + if (counter != LAT) counter <= counter + 1; + end + + assign access = (counter == LAT) && !input_diff; + +`ifndef SYNTHESIS + assign rdata = (^addr_ram !== 1'bx) && memory.exists(addr_ram) ? memory[addr_ram] : MEM_DEFAULT; +`else + assign rdata = memory[addr_ram]; +`endif + assign busy_out = ~access; endmodule diff --git a/source_code/ram/ram_wrapper.sv b/source_code/ram/ram_wrapper.sv index 8e7f72cf7..70c7e0716 100644 --- a/source_code/ram/ram_wrapper.sv +++ b/source_code/ram/ram_wrapper.sv @@ -1,26 +1,26 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: ram_wrapper.sv -* -* Created by: John Skubic +* +* Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 -* Description: Ram wrapper should contain the ram module provided by the -* simulation environment being used. If no ram modules are +* Description: Ram wrapper should contain the ram module provided by the +* simulation environment being used. If no ram modules are * provided, an emulated ram module must be created. */ @@ -28,28 +28,29 @@ `include "component_selection_defines.vh" module ram_wrapper ( - input logic CLK, nRST, - generic_bus_if.generic_bus gen_bus_if + input logic CLK, + nRST, + generic_bus_if.generic_bus gen_bus_if ); - import rv32i_types_pkg::*; - - logic [RAM_ADDR_SIZE-3:0] word_addr; - assign word_addr = gen_bus_if.addr[WORD_SIZE-1:2]; + import rv32i_types_pkg::*; - ram_sim_model #( - .LAT(0), - .ENDIANNESS(BUS_ENDIANNESS), - .N_BYTES(4) - ) v_lat_ram ( - .CLK(CLK), - .nRST(nRST), - .wdata_in(gen_bus_if.wdata), - .addr_in(word_addr), - .byte_en_in(gen_bus_if.byte_en), - .wen_in(gen_bus_if.wen), - .ren_in(gen_bus_if.ren), - .rdata_out(gen_bus_if.rdata), - .busy_out(gen_bus_if.busy) - ); + logic [RAM_ADDR_SIZE-3:0] word_addr; + assign word_addr = gen_bus_if.addr[WORD_SIZE-1:2]; + + ram_sim_model #( + .LAT(0), + .ENDIANNESS(BUS_ENDIANNESS), + .N_BYTES(4) + ) v_lat_ram ( + .CLK(CLK), + .nRST(nRST), + .wdata_in(gen_bus_if.wdata), + .addr_in(word_addr), + .byte_en_in(gen_bus_if.byte_en), + .wen_in(gen_bus_if.wen), + .ren_in(gen_bus_if.ren), + .rdata_out(gen_bus_if.rdata), + .busy_out(gen_bus_if.busy) + ); endmodule diff --git a/source_code/risc_mgmt/extensions/crc32/crc32.sv b/source_code/risc_mgmt/extensions/crc32/crc32.sv index 04927f835..a9509e350 100644 --- a/source_code/risc_mgmt/extensions/crc32/crc32.sv +++ b/source_code/risc_mgmt/extensions/crc32/crc32.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,51 +20,46 @@ * Email: jskubic@purdue.edu * Date Created: 04/06/2017 * Description: CRC32 calculator. Takes one byte and keeps the running -* crc32 calculation. +* crc32 calculation. */ module crc32 ( - input logic CLK, nRST, - input logic[7:0] in_byte, - input logic reset, new_byte, - output logic done, - output rv32i_types_pkg::word_t result + input logic CLK, + nRST, + input logic [7:0] in_byte, + input logic reset, + new_byte, + output logic done, + output rv32i_types_pkg::word_t result ); -import rv32i_types_pkg::*; + import rv32i_types_pkg::*; -parameter POLY = 32'h04c1_1db7; -parameter POLY_REV = 32'hedb8_8320; -parameter POLY_REV_REC = 32'h8260_8edb; + parameter logic [31:0] POLY = 32'h04c1_1db7; + parameter logic [31:0] POLY_REV = 32'hedb8_8320; + parameter logic [31:0] POLY_REV_REC = 32'h8260_8edb; -word_t next_crc, curr_crc, mask; -logic update; -logic [3:0] count; + word_t next_crc, curr_crc, mask; + logic update; + logic [3:0] count; -assign result = ~curr_crc; -assign done = (count[3] & ~new_byte) | reset; -assign update = ~count[3]; -assign mask = {32{curr_crc[0]}}; -assign next_crc = (curr_crc >> 1) ^ (POLY_REV & mask); + assign result = ~curr_crc; + assign done = (count[3] & ~new_byte) | reset; + assign update = ~count[3]; + assign mask = {32{curr_crc[0]}}; + assign next_crc = (curr_crc >> 1) ^ (POLY_REV & mask); -always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) - curr_crc <= '1; - else if (reset) - curr_crc <= '1; - else if (new_byte) - curr_crc <= curr_crc ^ {24'h0, in_byte}; - else if (update) - curr_crc <= next_crc; -end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) curr_crc <= '1; + else if (reset) curr_crc <= '1; + else if (new_byte) curr_crc <= curr_crc ^ {24'h0, in_byte}; + else if (update) curr_crc <= next_crc; + end -always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) - count <= 4'b1000; - else if (new_byte) - count <= 4'b0000; - else if (update) - count <= count + 1; -end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) count <= 4'b1000; + else if (new_byte) count <= 4'b0000; + else if (update) count <= count + 1; + end endmodule diff --git a/source_code/risc_mgmt/extensions/crc32/crc32_decode.sv b/source_code/risc_mgmt/extensions/crc32/crc32_decode.sv index e9043aaaf..0b8effb70 100644 --- a/source_code/risc_mgmt/extensions/crc32/crc32_decode.sv +++ b/source_code/risc_mgmt/extensions/crc32/crc32_decode.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,35 +25,36 @@ `include "risc_mgmt_decode_if.vh" module crc32_decode ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_decode_if.ext dif, - //stage to stage connection - output crc32_pkg::decode_execute_t idex + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_decode_if.ext dif, + //stage to stage connection + output crc32_pkg::decode_execute_t idex ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; + + parameter logic [6:0] OPCODE = 7'b000_1011; - parameter OPCODE = 7'b000_1011; + rtype_t insn_rtype; + logic [9:0] funct10; - rtype_t insn_rtype; - logic [9:0] funct10; + // prevent this extension from accessing core + assign dif.insn_claim = (dif.insn[6:0] == OPCODE); + assign dif.mem_to_reg = 1'b0; - // prevent this extension from accessing core - assign dif.insn_claim = (dif.insn[6:0] == OPCODE); - assign dif.mem_to_reg = 1'b0; + // Decoding rtype + assign insn_rtype = rtype_t'(dif.insn); + assign funct10 = {insn_rtype.funct7, insn_rtype.funct3}; - // Decoding rtype - assign insn_rtype = rtype_t'(dif.insn); - assign funct10 = {insn_rtype.funct7, insn_rtype.funct3}; + // Assigning RMGMT-Decode IF + assign dif.rsel_s_0 = insn_rtype.rs1; + assign dif.rsel_s_1 = insn_rtype.rs2; + assign dif.rsel_d = insn_rtype.rd; - // Assigning RMGMT-Decode IF - assign dif.rsel_s_0 = insn_rtype.rs1; - assign dif.rsel_s_1 = insn_rtype.rs2; - assign dif.rsel_d = insn_rtype.rd; - - // Assigning CRC Decode-Execute IF - assign idex.reset = (funct10 == 10'h0) && dif.insn_claim; - assign idex.new_byte = (funct10 == 10'h1) && dif.insn_claim; + // Assigning CRC Decode-Execute IF + assign idex.reset = (funct10 == 10'h0) && dif.insn_claim; + assign idex.new_byte = (funct10 == 10'h1) && dif.insn_claim; endmodule diff --git a/source_code/risc_mgmt/extensions/crc32/crc32_execute.sv b/source_code/risc_mgmt/extensions/crc32/crc32_execute.sv index ccce7d478..8df718c71 100644 --- a/source_code/risc_mgmt/extensions/crc32/crc32_execute.sv +++ b/source_code/risc_mgmt/extensions/crc32/crc32_execute.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,32 +25,33 @@ `include "risc_mgmt_execute_if.vh" module crc32_execute ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_execute_if.ext eif, - //stage to stage connection - input crc32_pkg::decode_execute_t idex, - output crc32_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_execute_if.ext eif, + //stage to stage connection + input crc32_pkg::decode_execute_t idex, + output crc32_pkg::execute_memory_t exmem ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; - logic [7:0] in_byte; - logic reset, new_byte, done; - word_t result; + logic [7:0] in_byte; + logic reset, new_byte, done; + word_t result; - //prevent this extension from accessing the core - assign eif.exception = 1'b0; - assign eif.branch_jump = 1'b0; + //prevent this extension from accessing the core + assign eif.exception = 1'b0; + assign eif.branch_jump = 1'b0; - // Instantiation of CRC calculation block - crc32 CRC32 (.*); + // Instantiation of CRC calculation block + crc32 CRC32 (.*); - assign new_byte = eif.start & idex.new_byte; - assign reset = idex.reset; - assign in_byte = eif.rdata_s_0[7:0]; - assign eif.busy = ~done; - assign eif.reg_w = idex.new_byte & done; - assign eif.reg_wdata = result; + assign new_byte = eif.start & idex.new_byte; + assign reset = idex.reset; + assign in_byte = eif.rdata_s_0[7:0]; + assign eif.busy = ~done; + assign eif.reg_w = idex.new_byte & done; + assign eif.reg_wdata = result; endmodule diff --git a/source_code/risc_mgmt/extensions/crc32/crc32_memory.sv b/source_code/risc_mgmt/extensions/crc32/crc32_memory.sv index 7861488c2..86091d7d1 100644 --- a/source_code/risc_mgmt/extensions/crc32/crc32_memory.sv +++ b/source_code/risc_mgmt/extensions/crc32/crc32_memory.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,24 +19,25 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: Memory stage of crc32 ISA extension. +* Description: Memory stage of crc32 ISA extension. */ `include "risc_mgmt_memory_if.vh" module crc32_memory ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_memory_if.ext mif, - //stage to stage connection - input crc32_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_memory_if.ext mif, + //stage to stage connection + input crc32_pkg::execute_memory_t exmem ); - //prevent this extension from accessing the core - assign mif.exception = 1'b0; - assign mif.busy = 1'b0; - assign mif.reg_w = 1'b0; - assign mif.mem_ren = 1'b0; - assign mif.mem_wen = 1'b0; + //prevent this extension from accessing the core + assign mif.exception = 1'b0; + assign mif.busy = 1'b0; + assign mif.reg_w = 1'b0; + assign mif.mem_ren = 1'b0; + assign mif.mem_wen = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv b/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv index b3a0277d1..f1cfe0379 100644 --- a/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv +++ b/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv @@ -1,21 +1,24 @@ -module carry_save_adder -#( - parameter BIT_WIDTH = 32 -) -( - input logic [(BIT_WIDTH-1):0] x, - input logic [(BIT_WIDTH-1):0] y, - input logic [(BIT_WIDTH-1):0] z, - output logic [(BIT_WIDTH-1):0] cout, - output logic [(BIT_WIDTH-1):0] sum +module carry_save_adder #( + parameter int BIT_WIDTH = 32 +) ( + input logic [(BIT_WIDTH-1):0] x, + input logic [(BIT_WIDTH-1):0] y, + input logic [(BIT_WIDTH-1):0] z, + output logic [(BIT_WIDTH-1):0] cout, + output logic [(BIT_WIDTH-1):0] sum ); - genvar i; - logic [(BIT_WIDTH-1):0] c; - generate - for (i = 0; i < BIT_WIDTH; i = i + 1) - begin - full_adder FA (.x(x[i]), .y(y[i]), .cin(z[i]), .cout(c[i]), .sum(sum[i])); - end - endgenerate - assign cout = c << 1; + genvar i; + logic [(BIT_WIDTH-1):0] c; + generate + for (i = 0; i < BIT_WIDTH; i = i + 1) begin : g_mul_csa + full_adder FA ( + .x(x[i]), + .y(y[i]), + .cin(z[i]), + .cout(c[i]), + .sum(sum[i]) + ); + end + endgenerate + assign cout = c << 1; endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv b/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv index 946514ea1..9bce1660c 100644 --- a/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv +++ b/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv @@ -1,51 +1,38 @@ -module flex_counter_mul -# ( - parameter NUM_CNT_BITS = 4 -) -( - input wire clk, - input wire n_rst, - input wire clear, - input wire count_enable, - input reg [(NUM_CNT_BITS-1):0] rollover_val, - output reg [(NUM_CNT_BITS-1):0] count_out, - output reg rollover_flag +module flex_counter_mul #( + parameter int NUM_CNT_BITS = 4 +) ( + input wire clk, + input wire n_rst, + input wire clear, + input wire count_enable, + input reg [(NUM_CNT_BITS-1):0] rollover_val, + output reg [(NUM_CNT_BITS-1):0] count_out, + output reg rollover_flag ); - reg [(NUM_CNT_BITS-1):0] next_count; - reg next_flag; - always_ff @ (posedge clk, negedge n_rst) - begin - if (n_rst == 0) - begin - count_out <= '0; - rollover_flag <= 1'b0; - end else begin - count_out <= next_count; - rollover_flag <= next_flag; - end - end + reg [(NUM_CNT_BITS-1):0] next_count; + reg next_flag; + always_ff @(posedge clk, negedge n_rst) begin + if (n_rst == 0) begin + count_out <= '0; + rollover_flag <= 1'b0; + end else begin + count_out <= next_count; + rollover_flag <= next_flag; + end + end - always_comb - begin - if (clear == 1) - next_flag = 0; - else if (count_enable == 0) - next_flag = rollover_flag; - else if (count_out == (rollover_val - 1) & clear == 1'b0) - next_flag = 1; - else - next_flag = 0; - - - if(clear) - next_count = 0; - else if(count_enable) begin - if(count_out == rollover_val) - next_count = 1; - else - next_count = count_out + 1; - end else - next_count = count_out; - end + always_comb begin + if (clear == 1) next_flag = 0; + else if (count_enable == 0) next_flag = rollover_flag; + else if (count_out == (rollover_val - 1) & clear == 1'b0) next_flag = 1; + else next_flag = 0; + + + if (clear) next_count = 0; + else if (count_enable) begin + if (count_out == rollover_val) next_count = 1; + else next_count = count_out + 1; + end else next_count = count_out; + end endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/full_adder.sv b/source_code/risc_mgmt/extensions/rv32m/full_adder.sv index 96e6b6cbc..d531e031c 100644 --- a/source_code/risc_mgmt/extensions/rv32m/full_adder.sv +++ b/source_code/risc_mgmt/extensions/rv32m/full_adder.sv @@ -1,12 +1,11 @@ -module full_adder -( - input logic x, - input logic y, - input logic cin, - output logic cout, - output logic sum +module full_adder ( + input logic x, + input logic y, + input logic cin, + output logic cout, + output logic sum ); - assign sum = x ^ y ^ cin; - assign cout = (x & y) | (x & cin) | (y & cin); + assign sum = x ^ y ^ cin; + assign cout = (x & y) | (x & cin) | (y & cin); endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv b/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv index 3417cf8bb..0c4a0b4f4 100644 --- a/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv @@ -1,242 +1,347 @@ -// Pipelined multiplier - 32 bits -module pp_mul32 -( - input logic CLK, - input logic nRST, - input logic [31:0] multiplicand, - input logic [31:0] multiplier, - input logic [1:0] is_signed, - input logic start, - output logic finished, - output logic [63:0] product +// Pipelined multiplier - 32 bits +module pp_mul32 ( + input logic CLK, + input logic nRST, + input logic [31:0] multiplicand, + input logic [31:0] multiplier, + input logic [1:0] is_signed, + input logic start, + output logic finished, + output logic [63:0] product ); - //logic start_reg; - logic [31:0] multiplicand_reg; - logic [31:0] multiplier_reg; - logic [63:0] result; - logic [63:0] result2; - logic [63:0] temp_product; - logic [63:0] temp_product2; - logic [31:0] multiplicand_mod; - logic [31:0] multiplier_mod; - logic adjust_product; - logic [63:0] partial_product [15:0]; - logic [63:0] pp0, pp1, pp2, pp3, pp4, pp5, pp6, pp7, pp8, pp9, pp10, pp11, pp12, pp13, pp14, pp15; - logic [32:0] mul_plus2, mul_minus2, mul_minus1; - logic [63:0] pp [15:0]; - logic [32:0] modified_in; - logic [63:0] sum0, sum1, sum2, sum3, sum4, sum5, sum6, sum7, sum8, sum9, sum10, sum11, sum12, sum13; - logic [63:0] cout0, cout1, cout2, cout3, cout4, cout5, cout6, cout7, cout8, cout9, cout10, cout11, cout12, cout13; - logic [1:0] count; - logic mult_complete; - //logic [63:0] sum13_pip, cout13_pip; - logic [63:0] sum5_pip, cout5_pip, sum6_pip, cout6_pip, sum7_pip, cout7_pip; - logic [1:0] is_signed_reg; - logic done; - logic count_ena; - integer i, j; + //logic start_reg; + logic [31:0] multiplicand_reg; + logic [31:0] multiplier_reg; + logic [63:0] result; + logic [63:0] result2; + logic [63:0] temp_product; + logic [63:0] temp_product2; + logic [31:0] multiplicand_mod; + logic [31:0] multiplier_mod; + logic adjust_product; + logic [63:0] partial_product[16]; + logic [63:0] + pp0, pp1, pp2, pp3, pp4, pp5, pp6, pp7, pp8, pp9, pp10, pp11, pp12, pp13, pp14, pp15; + logic [32:0] mul_plus2, mul_minus2, mul_minus1; + logic [63:0] pp[16]; + logic [32:0] modified_in; + logic [63:0] + sum0, sum1, sum2, sum3, sum4, sum5, sum6, sum7, sum8, sum9, sum10, sum11, sum12, sum13; + logic [63:0] + cout0, + cout1, + cout2, + cout3, + cout4, + cout5, + cout6, + cout7, + cout8, + cout9, + cout10, + cout11, + cout12, + cout13; + logic [1:0] count; + logic mult_complete; + //logic [63:0] sum13_pip, cout13_pip; + logic [63:0] sum5_pip, cout5_pip, sum6_pip, cout6_pip, sum7_pip, cout7_pip; + logic [1:0] is_signed_reg; + logic done; + logic count_ena; + integer i, j; - always_ff @ (posedge CLK, negedge nRST) begin - if (nRST == 0) begin - multiplicand_reg <= '0; - multiplier_reg <= '0; - is_signed_reg <= '0; - end - else if (start) begin - multiplicand_reg <= multiplicand; - multiplier_reg <= multiplier; - is_signed_reg <= is_signed; - end - end - // Modify multiplicand and multiplier if they are signed - assign multiplicand_mod = is_signed_reg[1] && multiplicand_reg[31] ? (~(multiplicand_reg)+1) : multiplicand_reg; - assign multiplier_mod = is_signed_reg[0] && multiplier_reg[31] ? (~(multiplier_reg)+1) : multiplier_reg; - // Control signal to modify final product - assign adjust_product = (is_signed_reg[0] & multiplier_reg[31]) ^ (is_signed_reg[1] & multiplicand_reg[31]); - // For bit pair recoding part - assign mul_plus2 = multiplicand_mod + multiplicand_mod; - assign mul_minus2 = ~mul_plus2 + 1; - assign mul_minus1 = ~multiplicand_mod + 1; - assign modified_in = {multiplier_mod,1'b0}; + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + multiplicand_reg <= '0; + multiplier_reg <= '0; + is_signed_reg <= '0; + end else if (start) begin + multiplicand_reg <= multiplicand; + multiplier_reg <= multiplier; + is_signed_reg <= is_signed; + end + end + // Modify multiplicand and multiplier if they are signed + assign multiplicand_mod = is_signed_reg[1] && multiplicand_reg[31] ? + (~(multiplicand_reg)+1) : multiplicand_reg; + assign multiplier_mod = is_signed_reg[0] && multiplier_reg[31] ? + (~(multiplier_reg)+1) : multiplier_reg; + // Control signal to modify final product + assign adjust_product = (is_signed_reg[0] & multiplier_reg[31]) + ^ (is_signed_reg[1] & multiplicand_reg[31]); + // For bit pair recoding part + assign mul_plus2 = multiplicand_mod + multiplicand_mod; + assign mul_minus2 = ~mul_plus2 + 1; + assign mul_minus1 = ~multiplicand_mod + 1; + assign modified_in = {multiplier_mod, 1'b0}; + + // STAGE 1: BOOTH ENCODER + // Bit pair recoding to generate partial product + always_comb begin + for (i = 0; i < 32; i = i + 2) begin + case ({ + modified_in[i+2], modified_in[i+1], modified_in[i] + }) + 3'b000: pp[i/2] = '0; //0 + 3'b001: pp[i/2] = {{32'd0}, multiplicand_mod}; // +1M + 3'b010: pp[i/2] = {{32'd0}, multiplicand_mod}; // +1M + 3'b011: pp[i/2] = {{31'd0}, mul_plus2}; // +2M + 3'b100: + if (mul_minus2 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus2}; // -2M + 3'b101: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus1}; // -1M + 3'b110: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus1}; // -1M + 3'b111: pp[i/2] = '0; + endcase + end + end + // Shift partial product + always_comb begin + for (j = 0; j < 16; j = j + 1) begin + partial_product[j] = pp[j] << (2 * j); // Shift with multiple of 2 (Radix 4) + end + end - // STAGE 1: BOOTH ENCODER - // Bit pair recoding to generate partial product - always_comb begin - for (i = 0; i < 32; i = i + 2) begin - case ({modified_in[i+2], modified_in[i+1], modified_in[i]}) - 3'b000: - pp[i/2] = '0; //0 - 3'b001: - pp[i/2] = {{32'd0},multiplicand_mod}; // +1M - 3'b010: - pp[i/2] = {{32'd0},multiplicand_mod}; // +1M - 3'b011: - pp[i/2] = {{31'd0},mul_plus2}; // +2M - 3'b100: - if (mul_minus2 == 0) pp[i/2] = '0; - else pp[i/2] = {{31{1'b1}},mul_minus2}; // -2M - 3'b101: - if (mul_minus1 == 0) pp[i/2] = '0; - else pp[i/2] = {{31{1'b1}},mul_minus1}; // -1M - 3'b110: - if (mul_minus1 == 0) pp[i/2] = '0; - else pp[i/2] = {{31{1'b1}},mul_minus1}; // -1M - 3'b111: - pp[i/2] = '0; - endcase - end - end - // Shift partial product - always_comb begin - for (j = 0; j < 16; j = j + 1) - begin - partial_product[j] = pp[j] << (2*j); // Shift with multiple of 2 (Radix 4) - end - end + // Pipeline register before wallace tree + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + pp0 <= '0; + pp1 <= '0; + pp2 <= '0; + pp3 <= '0; + pp4 <= '0; + pp5 <= '0; + pp6 <= '0; + pp7 <= '0; + pp8 <= '0; + pp9 <= '0; + pp10 <= '0; + pp11 <= '0; + pp12 <= '0; + pp13 <= '0; + pp14 <= '0; + pp15 <= '0; + end else begin + pp0 <= partial_product[0]; + pp1 <= partial_product[1]; + pp2 <= partial_product[2]; + pp3 <= partial_product[3]; + pp4 <= partial_product[4]; + pp5 <= partial_product[5]; + pp6 <= partial_product[6]; + pp7 <= partial_product[7]; + pp8 <= partial_product[8]; + pp9 <= partial_product[9]; + pp10 <= partial_product[10]; + pp11 <= partial_product[11]; + pp12 <= partial_product[12]; + pp13 <= partial_product[13]; + pp14 <= partial_product[14]; + pp15 <= partial_product[15]; + end + end - // Pipeline register before wallace tree - always_ff @ (posedge CLK, negedge nRST) begin - if (nRST == 0) begin - pp0 <= '0; - pp1 <= '0; - pp2 <= '0; - pp3 <= '0; - pp4 <= '0; - pp5 <= '0; - pp6 <= '0; - pp7 <= '0; - pp8 <= '0; - pp9 <= '0; - pp10 <= '0; - pp11 <= '0; - pp12 <= '0; - pp13 <= '0; - pp14 <= '0; - pp15 <= '0; - end - else begin - pp0 <= partial_product[0]; - pp1 <= partial_product[1]; - pp2 <= partial_product[2]; - pp3 <= partial_product[3]; - pp4 <= partial_product[4]; - pp5 <= partial_product[5]; - pp6 <= partial_product[6]; - pp7 <= partial_product[7]; - pp8 <= partial_product[8]; - pp9 <= partial_product[9]; - pp10 <= partial_product[10]; - pp11 <= partial_product[11]; - pp12 <= partial_product[12]; - pp13 <= partial_product[13]; - pp14 <= partial_product[14]; - pp15 <= partial_product[15]; - end - end - - // STAGE 2: WALLACE TREE - // Layer 1 - carry_save_adder #(64) CSA0 (.x(pp0), .y(pp1), .z(pp2), .cout(cout0), .sum(sum0)); - carry_save_adder #(64) CSA1 (.x(pp3), .y(pp4), .z(pp5), .cout(cout1), .sum(sum1)); - carry_save_adder #(64) CSA2 (.x(pp6), .y(pp7), .z(pp8), .cout(cout2), .sum(sum2)); - carry_save_adder #(64) CSA3 (.x(pp9), .y(pp10), .z(pp11), .cout(cout3), .sum(sum3)); - carry_save_adder #(64) CSA4 (.x(pp12), .y(pp13), .z(pp14), .cout(cout4), .sum(sum4)); // remaining partialproduct 15 - // Layer 2 - carry_save_adder #(64) CSA5 (.x(cout0), .y(sum0), .z(cout1), .cout(cout5), .sum(sum5)); - carry_save_adder #(64) CSA6 (.x(sum1), .y(cout2), .z(sum2), .cout(cout6), .sum(sum6)); - carry_save_adder #(64) CSA7 (.x(cout3), .y(sum3), .z(cout4), .cout(cout7), .sum(sum7)); // remaining sum4 - // Pipeline register in wallace tree between layer 2 and layer 3 - always_ff @ (posedge CLK, negedge nRST) begin - if (nRST == 0) begin - cout5_pip <= '0; - sum5_pip <= '0; - cout6_pip <= '0; - sum6_pip <= '0; - cout7_pip <= '0; - sum7_pip <= '0; - end - else begin - cout5_pip <= cout5; - sum5_pip <= sum5; - cout6_pip <= cout6; - sum6_pip <= sum6; - cout7_pip <= cout7; - sum7_pip <= sum7; - end - end + // STAGE 2: WALLACE TREE + // Layer 1 + carry_save_adder #(64) CSA0 ( + .x(pp0), + .y(pp1), + .z(pp2), + .cout(cout0), + .sum(sum0) + ); + carry_save_adder #(64) CSA1 ( + .x(pp3), + .y(pp4), + .z(pp5), + .cout(cout1), + .sum(sum1) + ); + carry_save_adder #(64) CSA2 ( + .x(pp6), + .y(pp7), + .z(pp8), + .cout(cout2), + .sum(sum2) + ); + carry_save_adder #(64) CSA3 ( + .x(pp9), + .y(pp10), + .z(pp11), + .cout(cout3), + .sum(sum3) + ); + carry_save_adder #(64) CSA4 ( + .x(pp12), + .y(pp13), + .z(pp14), + .cout(cout4), + .sum(sum4) + ); // remaining partialproduct 15 + // Layer 2 + carry_save_adder #(64) CSA5 ( + .x(cout0), + .y(sum0), + .z(cout1), + .cout(cout5), + .sum(sum5) + ); + carry_save_adder #(64) CSA6 ( + .x(sum1), + .y(cout2), + .z(sum2), + .cout(cout6), + .sum(sum6) + ); + carry_save_adder #(64) CSA7 ( + .x(cout3), + .y(sum3), + .z(cout4), + .cout(cout7), + .sum(sum7) + ); // remaining sum4 + // Pipeline register in wallace tree between layer 2 and layer 3 + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + cout5_pip <= '0; + sum5_pip <= '0; + cout6_pip <= '0; + sum6_pip <= '0; + cout7_pip <= '0; + sum7_pip <= '0; + end else begin + cout5_pip <= cout5; + sum5_pip <= sum5; + cout6_pip <= cout6; + sum6_pip <= sum6; + cout7_pip <= cout7; + sum7_pip <= sum7; + end + end - // Layer 3 - carry_save_adder #(64) CSA8 (.x(cout5), .y(sum5), .z(cout6), .cout(cout8), .sum(sum8)); - carry_save_adder #(64) CSA9 (.x(sum6), .y(cout7), .z(sum7), .cout(cout9), .sum(sum9)); - // Layer 4 - carry_save_adder #(64) CSA10 (.x(cout8), .y(sum8), .z(cout9), .cout(cout10), .sum(sum10)); - carry_save_adder #(64) CSA11 (.x(sum9), .y(pp15), .z(sum4), .cout(cout11), .sum(sum11)); - // Layer 5 - carry_save_adder #(64) CSA12 (.x(cout10), .y(sum10), .z(cout11), .cout(cout12), .sum(sum12)); // remaining sum11 - // Layer 6 - carry_save_adder #(64) CSA13 (.x(cout12), .y(sum12), .z(sum11), .cout(cout13), .sum(sum13)); + // Layer 3 + carry_save_adder #(64) CSA8 ( + .x(cout5), + .y(sum5), + .z(cout6), + .cout(cout8), + .sum(sum8) + ); + carry_save_adder #(64) CSA9 ( + .x(sum6), + .y(cout7), + .z(sum7), + .cout(cout9), + .sum(sum9) + ); + // Layer 4 + carry_save_adder #(64) CSA10 ( + .x(cout8), + .y(sum8), + .z(cout9), + .cout(cout10), + .sum(sum10) + ); + carry_save_adder #(64) CSA11 ( + .x(sum9), + .y(pp15), + .z(sum4), + .cout(cout11), + .sum(sum11) + ); + // Layer 5 + carry_save_adder #(64) CSA12 ( + .x(cout10), + .y(sum10), + .z(cout11), + .cout(cout12), + .sum(sum12) + ); // remaining sum11 + // Layer 6 + carry_save_adder #(64) CSA13 ( + .x(cout12), + .y(sum12), + .z(sum11), + .cout(cout13), + .sum(sum13) + ); - // STAGE 3: NORMAL ADDER - flex_counter_mul #(2) FC (.clk(CLK), .n_rst(nRST), .clear(start), .count_enable(count_ena), .rollover_val(2'd2), .count_out(count), .rollover_flag(finished)); - assign temp_product = cout13 + sum13; - assign temp_product2 = is_signed_reg[0] == 0 && multiplier_reg[31] ? temp_product + ({{33{multiplicand_mod[31]}},multiplicand_mod} << 32) : temp_product; // plus extra 1M - assign result = adjust_product ? (~temp_product2)+1: temp_product2; - assign mult_complete = count == 2'd1 | count == 2'd2; - assign result2 = mult_complete ? result: '0; + // STAGE 3: NORMAL ADDER + flex_counter_mul #(2) FC ( + .clk(CLK), + .n_rst(nRST), + .clear(start), + .count_enable(count_ena), + .rollover_val(2'd2), + .count_out(count), + .rollover_flag(finished) + ); + assign temp_product = cout13 + sum13; + assign temp_product2 = is_signed_reg[0] == 0 && multiplier_reg[31] ? + temp_product + ({{33{multiplicand_mod[31]}},multiplicand_mod} << 32) + : temp_product; // plus extra 1M + assign result = adjust_product ? (~temp_product2) + 1 : temp_product2; + assign mult_complete = count == 2'd1 | count == 2'd2; + assign result2 = mult_complete ? result : '0; - always_ff @ (posedge CLK, negedge nRST) begin - if (nRST == 0) begin - product <= '0; - end - else begin - product <= result2; - end - end + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + product <= '0; + end else begin + product <= result2; + end + end - //Small FSM to control flex counter - typedef enum logic {IDLE, START} state_type; - state_type state, next_state; - always_ff @ (posedge CLK, negedge nRST) begin - if (nRST == 0) - state <= IDLE; - else - state <= next_state; - end + //Small FSM to control flex counter + typedef enum logic { + IDLE, + START + } state_t; + state_t state, next_state; + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) state <= IDLE; + else state <= next_state; + end - always_comb begin + always_comb begin /* - next_state = state; - case (state) - IDLE: begin - if (start) - next_state = START; - end - START: begin - if (finished) - next_state = IDLE; - end - endcase + next_state = state; + case (state) + IDLE: begin + if (start) + next_state = START; + end + START: begin + if (finished) + next_state = IDLE; + end + endcase */ next_state = state; - if(state == IDLE && start) begin + if (state == IDLE && start) begin next_state = START; - end else if(state == START && finished) begin + end else if (state == START && finished) begin next_state = IDLE; end else begin next_state = state; end - end + end + + always_comb begin + count_ena = 0; + case (state) + IDLE: begin + count_ena = 0; + end + START: begin + count_ena = ~finished; + end + endcase + end - always_comb begin - count_ena = 0; - case (state) - IDLE: begin - count_ena = 0; - end - START: begin - count_ena = ~finished; - end - endcase - end - endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv b/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv index 6665a1e9f..361b60b6c 100644 --- a/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv +++ b/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv @@ -1,140 +1,141 @@ -module radix4_divider -#( - parameter NUM_BITS = 32 -) -( - input logic CLK, - input logic nRST, - input logic start, - input logic is_signed, //new - input logic [NUM_BITS-1:0] dividend, - input logic [NUM_BITS-1:0] divisor, - output logic [NUM_BITS-1:0] quotient, - output logic [NUM_BITS-1:0] remainder, - output logic finished +module radix4_divider #( + parameter int NUM_BITS = 32 +) ( + input logic CLK, + input logic nRST, + input logic start, + input logic is_signed, //new + input logic [NUM_BITS-1:0] dividend, + input logic [NUM_BITS-1:0] divisor, + output logic [NUM_BITS-1:0] quotient, + output logic [NUM_BITS-1:0] remainder, + output logic finished ); - logic [NUM_BITS-1:0] next_remainder, next_quotient, shifted_remainder, shifted_quotient, temp_quotient, temp_remainder; - logic [NUM_BITS:0] Result1, Result2, Result3; - logic [NUM_BITS-1:0] DivisorX2, DivisorX3; - logic [4:0] count, next_count; - - logic [NUM_BITS-1:0] usign_divisor, usign_dividend; - logic adjustment_possible, adjust_quotient, adjust_remainder; - logic div_done; - - assign usign_divisor = is_signed & divisor[NUM_BITS-1] ? (~divisor)+1 : divisor; - assign usign_dividend = is_signed & dividend[NUM_BITS-1] ? (~dividend)+1 : dividend; - assign adjustment_possible = is_signed && (divisor[NUM_BITS-1] ^ dividend[NUM_BITS-1]); - assign adjust_quotient = adjustment_possible && ~quotient[NUM_BITS-1]; - assign adjust_remainder = is_signed && dividend[NUM_BITS-1]; - assign div_done = (count == 0); - assign quotient = temp_quotient; - assign remainder = temp_remainder; - -/* - always_comb begin - quotient = temp_quotient; - remainder = temp_remainder; - if (count == 5'b1) begin - quotient = adjust_quotient ? ~temp_quotient + 1 : temp_quotient; - remainder = adjust_remainder ? ~temp_remainder + 1 : temp_remainder; - end - end + logic [NUM_BITS-1:0] + next_remainder, + next_quotient, + shifted_remainder, + shifted_quotient, + temp_quotient, + temp_remainder; + logic [NUM_BITS:0] Result1, Result2, Result3; + logic [NUM_BITS-1:0] DivisorX2, DivisorX3; + logic [4:0] count, next_count; + + logic [NUM_BITS-1:0] usign_divisor, usign_dividend; + logic adjustment_possible, adjust_quotient, adjust_remainder; + logic div_done; + + assign usign_divisor = is_signed & divisor[NUM_BITS-1] ? (~divisor) + 1 : divisor; + assign usign_dividend = is_signed & dividend[NUM_BITS-1] ? (~dividend) + 1 : dividend; + assign adjustment_possible = is_signed && (divisor[NUM_BITS-1] ^ dividend[NUM_BITS-1]); + assign adjust_quotient = adjustment_possible && ~quotient[NUM_BITS-1]; + assign adjust_remainder = is_signed && dividend[NUM_BITS-1]; + assign div_done = (count == 0); + assign quotient = temp_quotient; + assign remainder = temp_remainder; + + /* + always_comb begin + quotient = temp_quotient; + remainder = temp_remainder; + if (count == 5'b1) begin + quotient = adjust_quotient ? ~temp_quotient + 1 : temp_quotient; + remainder = adjust_remainder ? ~temp_remainder + 1 : temp_remainder; + end + end +*/ + /* + always_ff @(posedge CLK, negedge nRST) begin + if (~finished && adjust_quotient) + quotient <= ~quotient + 1; + + else if(~finished && adjust_remainder ) + remainder <= ~remainder + 1; + + else begin + quotient <= quotient; + remainder <= remainder; + end + end */ -/* - always_ff @(posedge CLK, negedge nRST) begin - if (~finished && adjust_quotient) - quotient <= ~quotient + 1; - - else if(~finished && adjust_remainder ) - remainder <= ~remainder + 1; - - else begin - quotient <= quotient; - remainder <= remainder; - end - end -*/ - - always_ff @(posedge CLK, negedge nRST) begin + + always_ff @(posedge CLK, negedge nRST) begin if (nRST == 0) begin - finished <= 1'b0; - end else if(start) begin finished <= 1'b0; - end else if(div_done) begin + end else if (start) begin + finished <= 1'b0; + end else if (div_done) begin finished <= 1'b1; end - end - //initialize d2 d3 - assign DivisorX2 = usign_divisor << 1; //Divisor*2 - assign DivisorX3 = (usign_divisor << 1) + usign_divisor; //Divisor*3 - always_ff @(posedge CLK, negedge nRST) begin - if (nRST == 0) begin - - count <= 5'd16; - temp_quotient <= '0; - temp_remainder <= '0; - end else if (start) begin - temp_quotient <= usign_dividend; - temp_remainder <= '0; - count <= 5'd16; - - end else if (count >= 0) begin - temp_quotient <= next_quotient; - temp_remainder <= next_remainder; - count <= next_count; - end - end - - always_comb begin - - next_quotient = temp_quotient; - next_remainder = temp_remainder; - next_count = count; - - if (count != 0) begin - next_count = count - 1; - shifted_remainder = (temp_remainder << 2) | temp_quotient[NUM_BITS-1:NUM_BITS-2]; - shifted_quotient = temp_quotient << 2; - Result1 = shifted_remainder - usign_divisor; - Result2 = shifted_remainder - DivisorX2; - Result3 = shifted_remainder - DivisorX3; - if(Result1[NUM_BITS-1] | Result1[NUM_BITS]) begin - next_remainder = shifted_remainder; - next_quotient = shifted_quotient | 0; - if (count == 1 && adjust_quotient ) - next_quotient = ~next_quotient + 1; - - if(count == 1 && adjust_remainder ) - next_remainder = ~next_remainder + 1; - - end else if(Result2[NUM_BITS-1] | Result2[NUM_BITS]) begin - next_remainder = Result1; - next_quotient = shifted_quotient | 1; - if (count == 1 && adjust_quotient ) - next_quotient = ~next_quotient + 1; - - if(count == 1 && adjust_remainder ) - next_remainder = ~next_remainder + 1; - end else if(Result3[NUM_BITS-1] | Result3[NUM_BITS]) begin - next_remainder = Result2; - next_quotient = shifted_quotient | 2; - if (count == 1 && adjust_quotient ) - next_quotient = ~next_quotient + 1; - - if(count == 1 && adjust_remainder ) - next_remainder = ~next_remainder + 1; - end else begin - next_remainder = Result3; - next_quotient = shifted_quotient | 3; - if (count == 1 && adjust_quotient ) - next_quotient = ~next_quotient + 1; - - if(count == 1 && adjust_remainder ) - next_remainder = ~next_remainder + 1; - end - end - - end + end + //initialize d2 d3 + assign DivisorX2 = usign_divisor << 1; //Divisor*2 + assign DivisorX3 = (usign_divisor << 1) + usign_divisor; //Divisor*3 + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + + count <= 5'd16; + temp_quotient <= '0; + temp_remainder <= '0; + end else if (start) begin + temp_quotient <= usign_dividend; + temp_remainder <= '0; + count <= 5'd16; + + end else begin + temp_quotient <= next_quotient; + temp_remainder <= next_remainder; + count <= next_count; + end + end + + always_comb begin + + next_quotient = temp_quotient; + next_remainder = temp_remainder; + next_count = count; + shifted_remainder = '0; + shifted_quotient = '0; + Result1 = '0; + Result2 = '0; + Result3 = '0; + + if (count != 0) begin + next_count = count - 1; + shifted_remainder = (temp_remainder << 2) | temp_quotient[NUM_BITS-1:NUM_BITS-2]; + shifted_quotient = temp_quotient << 2; + Result1 = shifted_remainder - usign_divisor; + Result2 = shifted_remainder - DivisorX2; + Result3 = shifted_remainder - DivisorX3; + if (Result1[NUM_BITS-1] | Result1[NUM_BITS]) begin + next_remainder = shifted_remainder; + next_quotient = shifted_quotient | 0; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + + end else if (Result2[NUM_BITS-1] | Result2[NUM_BITS]) begin + next_remainder = Result1[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 1; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end else if (Result3[NUM_BITS-1] | Result3[NUM_BITS]) begin + next_remainder = Result2[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 2; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end else begin + next_remainder = Result3[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 3; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end + end + + end endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/rv32m_decode.sv b/source_code/risc_mgmt/extensions/rv32m/rv32m_decode.sv index 371192b7d..4ba4cc713 100644 --- a/source_code/risc_mgmt/extensions/rv32m/rv32m_decode.sv +++ b/source_code/risc_mgmt/extensions/rv32m/rv32m_decode.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,46 +19,46 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: Decoding for standard multiply extension +* Description: Decoding for standard multiply extension */ `include "risc_mgmt_decode_if.vh" module rv32m_decode ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_decode_if.ext dif, - //stage to stage connection - output rv32m_pkg::decode_execute_t idex + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_decode_if.ext dif, + //stage to stage connection + output rv32m_pkg::decode_execute_t idex ); - import rv32m_pkg::*; + import rv32m_pkg::*; + + rv32m_insn_t insn; - parameter OPCODE = RV32M_OPCODE; + assign dif.mem_to_reg = 1'b0; - rv32m_insn_t insn; + assign insn = rv32m_insn_t'(dif.insn); - assign dif.mem_to_reg = 1'b0; - - assign insn = rv32m_insn_t'(dif.insn); + assign dif.insn_claim = (insn.opcode_major == RV32M_OPCODE) + && (insn.opcode_minor == RV32M_OPCODE_MINOR); + assign dif.rsel_s_0 = insn.rs1; + assign dif.rsel_s_1 = insn.rs2; + assign dif.rsel_d = insn.rd; - assign dif.insn_claim = (insn.opcode_major == OPCODE) && (insn.opcode_minor == RV32M_OPCODE_MINOR); - assign dif.rsel_s_0 = insn.rs1; - assign dif.rsel_s_1 = insn.rs2; - assign dif.rsel_d = insn.rd; - - // decode funct - assign idex.start = dif.insn_claim; - assign idex.mul = ~insn.funct[2]; - assign idex.div = insn.funct[2:1] == 2'b10; - assign idex.rem = insn.funct[2:1] == 2'b11; + // decode funct + assign idex.start = dif.insn_claim; + assign idex.mul = ~insn.funct[2]; + assign idex.div = insn.funct[2:1] == 2'b10; + assign idex.rem = insn.funct[2:1] == 2'b11; - assign idex.usign_usign = (insn.funct == 3'b011) || (insn.funct == 3'b101) - || (insn.funct == 3'b111); - assign idex.sign_sign = (insn.funct == 3'b001) || (insn.funct == 3'b100) + assign idex.usign_usign = (insn.funct == 3'b011) || (insn.funct == 3'b101) + || (insn.funct == 3'b111); + assign idex.sign_sign = (insn.funct == 3'b001) || (insn.funct == 3'b100) || (insn.funct == 3'b110) || (insn.funct == 3'b000); - assign idex.sign_usign = insn.funct == 3'b010; + assign idex.sign_usign = insn.funct == 3'b010; - assign idex.lower_word = ~(|insn.funct[1:0]); // only valid for mul + assign idex.lower_word = ~(|insn.funct[1:0]); // only valid for mul endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv b/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv index edc4ee8d6..b3ba5660c 100644 --- a/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv +++ b/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,148 +19,149 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: Execute stage for standard RV32M +* Description: Execute stage for standard RV32M */ `include "risc_mgmt_execute_if.vh" module rv32m_execute ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_execute_if.ext eif, - //stage to stage connection - input rv32m_pkg::decode_execute_t idex, - output rv32m_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_execute_if.ext eif, + //stage to stage connection + input rv32m_pkg::decode_execute_t idex, + output rv32m_pkg::execute_memory_t exmem ); - import rv32m_pkg::*; - import rv32i_types_pkg::*; + import rv32m_pkg::*; + import rv32i_types_pkg::*; - /* Static RISC-MGMT assignments */ + /* Static RISC-MGMT assignments */ - assign eif.exception = 1'b0; - assign eif.reg_w = 1'b1; - assign eif.branch_jump = 1'b0; + assign eif.exception = 1'b0; + assign eif.reg_w = 1'b1; + assign eif.branch_jump = 1'b0; - /* Operand Saver to detect new request */ + /* Operand Saver to detect new request */ - // operand saver - word_t op_a, op_b, op_a_save, op_b_save; - logic [2:0] operation, operation_save; - logic [1:0] is_signed_save, is_signed_curr, is_signed; - logic operand_diff; + // operand saver + word_t op_a, op_b, op_a_save, op_b_save; + logic [2:0] operation, operation_save; + logic [1:0] is_signed_save, is_signed_curr, is_signed; + logic operand_diff; - assign op_a = operand_diff ? eif.rdata_s_0 : op_a_save; - assign op_b = operand_diff ? eif.rdata_s_1 : op_b_save; - assign operand_diff = ((op_a_save != eif.rdata_s_0) || + assign op_a = operand_diff ? eif.rdata_s_0 : op_a_save; + assign op_b = operand_diff ? eif.rdata_s_1 : op_b_save; + assign operand_diff = ((op_a_save != eif.rdata_s_0) || (op_b_save != eif.rdata_s_1) || - (is_signed_save != is_signed_curr) || + (is_signed_save != is_signed_curr) || (operation_save != {idex.mul, idex.div, idex.rem})) && idex.start ; - assign is_signed_curr = idex.usign_usign ? 2'b00 : ( - idex.sign_sign ? 2'b11 : 2'b10); - assign is_signed = operand_diff ? is_signed_curr : is_signed_save; - assign operation = operand_diff ? {idex.mul, idex.div, idex.rem} : operation_save; - - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) begin - op_a_save <= '0; - op_b_save <= '0; - is_signed_save <= '0; - operation_save <= '0; + assign is_signed_curr = idex.usign_usign ? 2'b00 : (idex.sign_sign ? 2'b11 : 2'b10); + assign is_signed = operand_diff ? is_signed_curr : is_signed_save; + assign operation = operand_diff ? {idex.mul, idex.div, idex.rem} : operation_save; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + op_a_save <= '0; + op_b_save <= '0; + is_signed_save <= '0; + operation_save <= '0; + end else if (operand_diff) begin + op_a_save <= eif.rdata_s_0; + op_b_save <= eif.rdata_s_1; + is_signed_save <= is_signed_curr; + operation_save <= {idex.mul, idex.div, idex.rem}; + end end - else if (operand_diff) begin - op_a_save <= eif.rdata_s_0; - op_b_save <= eif.rdata_s_1; - is_signed_save <= is_signed_curr; - operation_save <= {idex.mul, idex.div, idex.rem}; + + + /* MULTIPLICATION */ + + // multiplier signals + word_t multiplicand, multiplier; + logic [(WORD_SIZE*2)-1:0] product; + logic mul_finished; + logic mul_start; + + assign multiplicand = op_a; + assign multiplier = op_b; + assign mul_start = operand_diff && operation[2]; + + // Module instantiations + pp_mul32 mult_i ( + .CLK(CLK), + .nRST(nRST), + .multiplicand(multiplicand), + .multiplier(multiplier), + .product(product), + .is_signed(is_signed), + .start(mul_start), + .finished(mul_finished) + ); + + + /* DIVISION / REMAINDER */ + + logic overflow, div_zero, div_finished; + word_t divisor, dividend, quotient, remainder, divisor_save, dividend_save; + logic div_operand_diff; + logic div_start; + + assign divisor = op_b; + assign dividend = op_a; + assign overflow = (dividend == 32'h8000_0000) && (divisor == 32'hffff_ffff) && idex.sign_sign; + assign div_zero = (divisor == 32'h0); + assign div_start = operand_diff && ~operation[2] & ~overflow & ~div_zero; + + radix4_divider div_i ( + .CLK(CLK), + .nRST(nRST), + .divisor(divisor), + .dividend(dividend), + .is_signed(idex.sign_sign), + .start(div_start), + .remainder(remainder), + .quotient(quotient), + .finished(div_finished) + ); + + /* Result */ + + always_comb begin + casez (operation) + 3'b1??: begin // MUL + eif.busy = ~mul_finished; + eif.reg_wdata = idex.lower_word ? + product[WORD_SIZE-1:0] + : product[(WORD_SIZE*2)-1 : WORD_SIZE]; + end + 3'b01?: begin // DIV + eif.busy = ~div_finished & ~(div_zero | overflow); + if (div_zero) begin + eif.reg_wdata = idex.sign_sign ? 32'hffff_ffff : 32'h7fff_ffff; + end else if (overflow) begin + eif.reg_wdata = 32'h8000_0000; + end else begin + eif.reg_wdata = quotient; + end + end + 3'b001: begin // REM + eif.busy = ~div_finished & ~(div_zero | overflow); + if (div_zero) begin + eif.reg_wdata = dividend; + end else if (overflow) begin + eif.reg_wdata = 32'h0000_0000; + end else begin + eif.reg_wdata = remainder; + end + end + default: begin + eif.busy = 1'b0; + eif.reg_wdata = 32'hBAD3_BAD3; + end + endcase end - end - - - /* MULTIPLICATION */ - - // multiplier signals - word_t multiplicand, multiplier; - logic [(WORD_SIZE*2)-1:0] product; - logic mul_finished; - logic mul_start; - - assign multiplicand = op_a; - assign multiplier = op_b; - assign mul_start = operand_diff && operation[2]; - - // Module instantiations - pp_mul32 mult_i ( - .CLK(CLK), - .nRST(nRST), - .multiplicand(multiplicand), - .multiplier(multiplier), - .product(product), - .is_signed(is_signed), - .start(mul_start), - .finished(mul_finished) - ); - - - /* DIVISION / REMAINDER */ - - logic overflow, div_zero, div_finished; - word_t divisor, dividend, quotient, remainder, divisor_save, dividend_save; - logic div_operand_diff; - logic div_start; - - assign divisor = op_b; - assign dividend = op_a; - assign overflow = (dividend == 32'h8000_0000) && (divisor == 32'hffff_ffff) && idex.sign_sign; - assign div_zero = (divisor == 32'h0); - assign div_start = operand_diff && ~operation[2] & ~overflow & ~div_zero; - - radix4_divider div_i( - .CLK(CLK), - .nRST(nRST), - .divisor(divisor), - .dividend(dividend), - .is_signed(idex.sign_sign), - .start(div_start), - .remainder(remainder), - .quotient(quotient), - .finished(div_finished) - ); - - /* Result */ - - always_comb begin - casez (operation) - 3'b1?? : begin // MUL - eif.busy = ~mul_finished; - eif.reg_wdata = idex.lower_word ? product[WORD_SIZE-1:0] : product[(WORD_SIZE*2)-1 : WORD_SIZE]; - end - 3'b01? : begin // DIV - eif.busy = ~div_finished & ~(div_zero | overflow); - if(div_zero) begin - eif.reg_wdata = idex.sign_sign ? 32'hffff_ffff : 32'h7fff_ffff; - end else if (overflow) begin - eif.reg_wdata = 32'h8000_0000; - end else begin - eif.reg_wdata = quotient; - end - end - 3'b001 : begin // REM - eif.busy = ~div_finished & ~(div_zero | overflow); - if(div_zero) begin - eif.reg_wdata = dividend; - end else if (overflow) begin - eif.reg_wdata = 32'h0000_0000; - end else begin - eif.reg_wdata = remainder; - end - end - default : begin - eif.busy = 1'b0; - eif.reg_wdata = 32'hBAD3_BAD3; - end - endcase - end endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/rv32m_memory.sv b/source_code/risc_mgmt/extensions/rv32m/rv32m_memory.sv index 5e952455f..52b5a1b67 100644 --- a/source_code/risc_mgmt/extensions/rv32m/rv32m_memory.sv +++ b/source_code/risc_mgmt/extensions/rv32m/rv32m_memory.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,24 +20,25 @@ * Email: jskubic@purdue.edu * Date Created: 02/07/2017 * Description: Memory stage for standard RV32M extension -* This stage will do nothing. +* This stage will do nothing. */ `include "risc_mgmt_memory_if.vh" module rv32m_memory ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_memory_if.ext mif, - //stage to stage connection - input rv32m_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_memory_if.ext mif, + //stage to stage connection + input rv32m_pkg::execute_memory_t exmem ); - //prevent this extension from accessing the core - assign mif.exception = 1'b0; - assign mif.busy = 1'b0; - assign mif.reg_w = 1'b0; - assign mif.mem_ren = 1'b0; - assign mif.mem_wen = 1'b0; + //prevent this extension from accessing the core + assign mif.exception = 1'b0; + assign mif.busy = 1'b0; + assign mif.reg_w = 1'b0; + assign mif.mem_ren = 1'b0; + assign mif.mem_wen = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/shift_add_multiplier.sv b/source_code/risc_mgmt/extensions/rv32m/shift_add_multiplier.sv index a3c845637..41227fb89 100644 --- a/source_code/risc_mgmt/extensions/rv32m/shift_add_multiplier.sv +++ b/source_code/risc_mgmt/extensions/rv32m/shift_add_multiplier.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,60 +20,59 @@ * Email: jskubic@purdue.edu * Date Created: 02/15/2017 * Description: N bit parameterized shift and add multiplier -* Takes up to N+1 cycles to compute the product. +* Takes up to N+1 cycles to compute the product. */ -module shift_add_multiplier # ( - parameter N = 32 -) -( - input logic CLK, nRST, - input logic [N-1:0] multiplicand, - input logic [N-1:0] multiplier, - input logic [1:0] is_signed, - input logic start, - output logic [(N*2)-1:0] product, - output logic finished +module shift_add_multiplier #( + parameter int N = 32 +) ( + input logic CLK, + nRST, + input logic [N-1:0] multiplicand, + input logic [N-1:0] multiplier, + input logic [1:0] is_signed, + input logic start, + output logic [(N*2)-1:0] product, + output logic finished ); - logic [(N*2)-1:0] multiplier_reg, multiplicand_reg; - logic [(N*2)-1:0] multiplier_ext, multiplicand_ext; - logic [(N*2)-1:0] partial_product; - logic mult_complete, adjust_product; + logic [(N*2)-1:0] multiplier_reg, multiplicand_reg; + logic [(N*2)-1:0] multiplier_ext, multiplicand_ext; + logic [(N*2)-1:0] partial_product; + logic mult_complete, adjust_product; - assign mult_complete = !(|multiplier_reg); - assign adjust_product = (is_signed[0] & multiplier[N-1]) ^ (is_signed[1] & multiplicand[N-1]); - assign partial_product = multiplier_reg[0] ? multiplicand_reg : '0; - assign multiplier_ext = (~{{N{multiplier[N-1]}},multiplier}) + 1; - assign multiplicand_ext = (~{{N{multiplicand[N-1]}},multiplicand}) + 1; + assign mult_complete = !(|multiplier_reg); + assign adjust_product = (is_signed[0] & multiplier[N-1]) ^ (is_signed[1] & multiplicand[N-1]); + assign partial_product = multiplier_reg[0] ? multiplicand_reg : '0; + assign multiplier_ext = (~{{N{multiplier[N-1]}},multiplier}) + 1; + assign multiplicand_ext = (~{{N{multiplicand[N-1]}},multiplicand}) + 1; - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - finished <= 1'b0; - else if (start) - finished <= 1'b0; - else if (mult_complete) - finished <= 1'b1; - end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) finished <= 1'b0; + else if (start) finished <= 1'b0; + else if (mult_complete) finished <= 1'b1; + end - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) begin - multiplicand_reg <= '0; - multiplier_reg <= '0; - product <= '0; - end else if(start) begin - multiplicand_reg <= (is_signed[1] && multiplicand[N-1]) ? multiplicand_ext : {{N{1'b0}}, multiplicand}; - multiplier_reg <= (is_signed[0] && multiplier[N-1]) ? multiplier_ext : {{N{1'b0}}, multiplier}; - product <= '0; - end else if (mult_complete & ~finished) begin // adjust sign on product - multiplicand_reg <= multiplicand_reg; - multiplier_reg <= multiplier_reg; - product <= adjust_product ? (~product)+1 : product; - end else if (~finished) begin - multiplicand_reg <= multiplicand_reg << 1; - multiplier_reg <= multiplier_reg >> 1; - product <= product + partial_product; + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + multiplicand_reg <= '0; + multiplier_reg <= '0; + product <= '0; + end else if (start) begin + multiplicand_reg <= (is_signed[1] && multiplicand[N-1]) ? + multiplicand_ext : {{N{1'b0}}, multiplicand}; + multiplier_reg <= (is_signed[0] && multiplier[N-1]) ? + multiplier_ext : {{N{1'b0}}, multiplier}; + product <= '0; + end else if (mult_complete & ~finished) begin // adjust sign on product + multiplicand_reg <= multiplicand_reg; + multiplier_reg <= multiplier_reg; + product <= adjust_product ? (~product) + 1 : product; + end else if (~finished) begin + multiplicand_reg <= multiplicand_reg << 1; + multiplier_reg <= multiplier_reg >> 1; + product <= product + partial_product; + end end - end endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/shift_test_restore_divider.sv b/source_code/risc_mgmt/extensions/rv32m/shift_test_restore_divider.sv index eda79f514..ba9e441cd 100644 --- a/source_code/risc_mgmt/extensions/rv32m/shift_test_restore_divider.sv +++ b/source_code/risc_mgmt/extensions/rv32m/shift_test_restore_divider.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,73 +23,70 @@ */ module shift_test_restore_divider #( - parameter N = 32 -)( - input logic CLK, nRST, - input logic [N-1:0] divisor, dividend, - input logic is_signed, - input logic start, - output logic [N-1:0] remainder, quotient, - output logic finished + parameter int N = 32 +) ( + input logic CLK, + nRST, + input logic [N-1:0] divisor, + dividend, + input logic is_signed, + input logic start, + output logic [N-1:0] remainder, + quotient, + output logic finished ); - localparam COUNTER_BITS = $clog2(N) + 1; - localparam U_Q = N-1; - localparam U_R = (2*N)-1; + localparam int COUNTER_BITS = $clog2(N) + 1; + localparam int U_Q = N - 1; + localparam int U_R = (2 * N) - 1; - logic [(2*N)+1:0] result; - assign {remainder, quotient} = result[(2*N)-1:0]; - logic test_phase; - logic [COUNTER_BITS-1:0] counter; - logic [N-1:0] usign_divisor, usign_dividend; - logic adjustment_possible, adjust_quotient, adjust_remainder; - logic div_done; + logic [(2*N)+1:0] result; + assign {remainder, quotient} = result[(2*N)-1:0]; + logic test_phase; + logic [COUNTER_BITS-1:0] counter; + logic [N-1:0] usign_divisor, usign_dividend; + logic adjustment_possible, adjust_quotient, adjust_remainder; + logic div_done; - assign usign_divisor = is_signed & divisor[N-1] ? (~divisor)+1 : divisor; - assign usign_dividend = is_signed & dividend[N-1] ? (~dividend)+1 : dividend; - assign adjustment_possible = is_signed && (divisor[N-1] ^ dividend[N-1]); - assign adjust_quotient = adjustment_possible && ~quotient[N-1]; - assign adjust_remainder = is_signed && dividend[N-1]; - assign div_done = (counter == 0); + assign usign_divisor = is_signed & divisor[N-1] ? (~divisor) + 1 : divisor; + assign usign_dividend = is_signed & dividend[N-1] ? (~dividend) + 1 : dividend; + assign adjustment_possible = is_signed && (divisor[N-1] ^ dividend[N-1]); + assign adjust_quotient = adjustment_possible && ~quotient[N-1]; + assign adjust_remainder = is_signed && dividend[N-1]; + assign div_done = (counter == 0); - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) begin - result <= '0; - counter <= N; - test_phase <= 1'b0; - end else if (start) begin - result <= {{(N-1){1'b0}}, usign_dividend, 1'b0}; - counter <= N; - test_phase <= 1'b0; - end else if (counter > 0) begin - if(~test_phase) begin // shift and sub - result[U_R+1-:N+1] <= result[U_R+1-:N+1] - usign_divisor; - end else begin // check result - counter <= counter - 1; - if(result[U_R+1]) // negative remainder, must restore - result <= {(result[U_R+1-:N+1] + usign_divisor), result[U_Q:0]} << 1; - else - result <= {result[U_R-1:0], 1'b1}; - end - test_phase <= ~test_phase; - end else if (~finished) begin - if(adjust_quotient) - result[U_Q:0] <= (~result[U_Q:0])+1; - if(adjust_remainder) - result[U_R-:N] <= (~result[U_R+1-:N])+1; - //result[U_R-:N] <= (~({result[U_R],result[U_R-:N-1]}))+1; - else - result[U_R-:N] <= result[U_R+1-:N]; + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + result <= '0; + counter <= N; + test_phase <= 1'b0; + end else if (start) begin + result <= {{(N - 1) {1'b0}}, usign_dividend, 1'b0}; + counter <= N; + test_phase <= 1'b0; + end else if (counter > 0) begin + if (~test_phase) begin // shift and sub + result[U_R+1-:N+1] <= result[U_R+1-:N+1] - usign_divisor; + end else begin // check result + counter <= counter - 1; + if (result[U_R+1]) // negative remainder, must restore + result <= {(result[U_R+1-:N+1] + usign_divisor), result[U_Q:0]} << 1; + else result <= {result[U_R-1:0], 1'b1}; + end + test_phase <= ~test_phase; + end else if (~finished) begin + if (adjust_quotient) result[U_Q:0] <= (~result[U_Q:0]) + 1; + if (adjust_remainder) result[U_R-:N] <= (~result[U_R+1-:N]) + 1; + //result[U_R-:N] <= (~({result[U_R],result[U_R-:N-1]}))+1; + else + result[U_R-:N] <= result[U_R+1-:N]; + end end - end - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) - finished <= 1'b0; - else if (start) - finished <= 1'b0; - else if (div_done) - finished <= 1'b1; - end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) finished <= 1'b0; + else if (start) finished <= 1'b0; + else if (div_done) finished <= 1'b1; + end endmodule diff --git a/source_code/risc_mgmt/extensions/template/template_decode.sv b/source_code/risc_mgmt/extensions/template/template_decode.sv index 2f68ad665..79f00d8f2 100644 --- a/source_code/risc_mgmt/extensions/template/template_decode.sv +++ b/source_code/risc_mgmt/extensions/template/template_decode.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,33 +16,34 @@ * * Filename: template_decode.sv * -* Created by: +* Created by: * Email: * Date Created: * Description: This extension is the Template for creating rytpe custom -* instructions. +* instructions. */ `include "risc_mgmt_decode_if.vh" module template_decode ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_decode_if.ext dif, - //stage to stage connection - output template_pkg::decode_execute_t idex + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_decode_if.ext dif, + //stage to stage connection + output template_pkg::decode_execute_t idex ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; - parameter OPCODE = 7'b000_1011; + parameter logic [7:0] OPCODE = 7'b000_1011; - rtype_t insn_rtype; - logic [9:0] funct10; + rtype_t insn_rtype; + logic [9:0] funct10; - // prevent this extension from accessing core - assign dif.insn_claim = 1'b0; // (dif.insn[6:0] == OPCODE); - assign funct10 = {insn_rtype.funct7, insn_rtype.funct3}; - assign dif.mem_to_reg = 1'b0; + // prevent this extension from accessing core + assign dif.insn_claim = 1'b0; // (dif.insn[6:0] == OPCODE); + assign funct10 = {insn_rtype.funct7, insn_rtype.funct3}; + assign dif.mem_to_reg = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/template/template_execute.sv b/source_code/risc_mgmt/extensions/template/template_execute.sv index 1f6f13932..aa9f6df59 100644 --- a/source_code/risc_mgmt/extensions/template/template_execute.sv +++ b/source_code/risc_mgmt/extensions/template/template_execute.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,26 +18,27 @@ * * Created by: * Email: -* Date Created: +* Date Created: * Description: This extension is the Template for creating rytpe custom -* instructions. +* instructions. */ `include "risc_mgmt_execute_if.vh" module template_execute ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_execute_if.ext eif, - //stage to stage connection - input template_pkg::decode_execute_t idex, - output template_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_execute_if.ext eif, + //stage to stage connection + input template_pkg::decode_execute_t idex, + output template_pkg::execute_memory_t exmem ); - //prevent this extension from accessing the core - assign eif.exception = 1'b0; - assign eif.busy = 1'b0; - assign eif.reg_w = 1'b0; - assign eif.branch_jump = 1'b0; + //prevent this extension from accessing the core + assign eif.exception = 1'b0; + assign eif.busy = 1'b0; + assign eif.reg_w = 1'b0; + assign eif.branch_jump = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/template/template_memory.sv b/source_code/risc_mgmt/extensions/template/template_memory.sv index 6eed54265..e5da82896 100644 --- a/source_code/risc_mgmt/extensions/template/template_memory.sv +++ b/source_code/risc_mgmt/extensions/template/template_memory.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,7 +18,7 @@ * * Created by: * Email: -* Date Created: +* Date Created: * Description: This extension is the Template for creating rytpe custom * instructions */ @@ -26,18 +26,19 @@ `include "risc_mgmt_memory_if.vh" module template_memory ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_memory_if.ext mif, - //stage to stage connection - input template_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_memory_if.ext mif, + //stage to stage connection + input template_pkg::execute_memory_t exmem ); - //prevent this extension from accessing the core - assign mif.exception = 1'b0; - assign mif.busy = 1'b0; - assign mif.reg_w = 1'b0; - assign mif.mem_ren = 1'b0; - assign mif.mem_wen = 1'b0; + //prevent this extension from accessing the core + assign mif.exception = 1'b0; + assign mif.busy = 1'b0; + assign mif.reg_w = 1'b0; + assign mif.mem_ren = 1'b0; + assign mif.mem_wen = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/wscript b/source_code/risc_mgmt/extensions/wscript deleted file mode 100644 index 260d8f489..000000000 --- a/source_code/risc_mgmt/extensions/wscript +++ /dev/null @@ -1,14 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -# Add your extension as a directory to recurse into here - -def configure(cnf): - cnf.recurse(template) - cnf.recurse(rv32m) - cnf.recurse(crc32) - -def sim_source(cnf): - cnf.recurse(template) - cnf.recurse(rv32m) - cnf.recurse(crc32) diff --git a/source_code/risc_mgmt/risc_mgmt.core b/source_code/risc_mgmt/risc_mgmt.core new file mode 100644 index 000000000..c43cc65ff --- /dev/null +++ b/source_code/risc_mgmt/risc_mgmt.core @@ -0,0 +1,33 @@ +CAPI=2: +name: socet:riscv:risc_mgmt:0.1.0 +description: risc management + +filesets: + rtl: + files: + - risc_mgmt_wrapper.sv + - extensions/crc32/crc32.sv + - extensions/crc32/crc32_decode.sv + - extensions/crc32/crc32_execute.sv + - extensions/crc32/crc32_memory.sv + - extensions/rv32m/rv32m_memory.sv + - extensions/rv32m/shift_add_multiplier.sv + - extensions/rv32m/shift_test_restore_divider.sv + - extensions/rv32m/carry_save_adder.sv + - extensions/rv32m/flex_counter_mul.sv + - extensions/rv32m/full_adder.sv + - extensions/rv32m/pp_mul32.sv + - extensions/rv32m/radix4_divider.sv + - extensions/rv32m/rv32m_execute.sv + - extensions/rv32m/rv32m_decode.sv + - extensions/template/template_decode.sv + - extensions/template/template_execute.sv + - extensions/template/template_memory.sv + - tspp/tspp_risc_mgmt.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + toplevel: risc_mgmt_wrapper \ No newline at end of file diff --git a/source_code/risc_mgmt/risc_mgmt_wrapper.sv b/source_code/risc_mgmt/risc_mgmt_wrapper.sv index 2e91676da..3feb965f5 100644 --- a/source_code/risc_mgmt/risc_mgmt_wrapper.sv +++ b/source_code/risc_mgmt/risc_mgmt_wrapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -26,10 +26,11 @@ `include "risc_mgmt_if.vh" module risc_mgmt_wrapper ( - input logic CLK, nRST, - risc_mgmt_if.ts_rmgmt rm_if + input logic CLK, + nRST, + risc_mgmt_if.ts_rmgmt rm_if ); - tspp_risc_mgmt risc_mgmt_i(.*); + tspp_risc_mgmt risc_mgmt_i (.*); endmodule diff --git a/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv b/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv index b859357ae..f06b9d5a0 100644 --- a/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv +++ b/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,8 +20,8 @@ * Email: jskubic@purdue.edu * Date Created: 02/07/2017 * Description: Top Level Module for RISC-MGMT -* Provides the interface between extensions -* and the standard core. +* Provides the interface between extensions +* and the standard core. * * This version will connect extensions * to a two stage pipeline (tspp). @@ -32,155 +32,152 @@ `include "risc_mgmt_if.vh" module tspp_risc_mgmt ( - input logic CLK, nRST, - risc_mgmt_if.ts_rmgmt rm_if + input logic CLK, + nRST, + risc_mgmt_if.ts_rmgmt rm_if ); - import rv32i_types_pkg::*; - - parameter N_EXTENSIONS = `NUM_EXTENSIONS; - localparam N_EXT_BITS = $clog2(N_EXTENSIONS); - - /****************************************************************** - * Signal Instantiations - ******************************************************************/ - - // Decode Stage Signals - word_t [N_EXTENSIONS-1:0] d_insn; - logic [N_EXTENSIONS-1:0] d_insn_claim; - logic [N_EXTENSIONS-1:0] d_mem_to_reg; // ignored in two stage pipe rmgmt - logic [N_EXTENSIONS-1:0][4:0] d_rsel_s_0; - logic [N_EXTENSIONS-1:0][4:0] d_rsel_s_1; - logic [N_EXTENSIONS-1:0][4:0] d_rsel_d; - - //Execute Stage Signals - logic [N_EXTENSIONS-1:0] e_start; - logic [N_EXTENSIONS-1:0] e_exception; - logic [N_EXTENSIONS-1:0] e_busy; - word_t [N_EXTENSIONS-1:0] e_rdata_s_0; - word_t [N_EXTENSIONS-1:0] e_rdata_s_1; - logic [N_EXTENSIONS-1:0] e_branch_jump; - word_t [N_EXTENSIONS-1:0] e_pc; - word_t [N_EXTENSIONS-1:0] e_br_j_addr; - word_t [N_EXTENSIONS-1:0] e_reg_wdata; - logic [N_EXTENSIONS-1:0] e_reg_w; - - //Memory Stage Signals - logic [N_EXTENSIONS-1:0] m_exception; - logic [N_EXTENSIONS-1:0] m_busy; - word_t [N_EXTENSIONS-1:0] m_mem_addr; - logic [N_EXTENSIONS-1:0] m_mem_ren; - logic [N_EXTENSIONS-1:0] m_mem_wen; - logic [N_EXTENSIONS-1:0] m_mem_busy; - logic [N_EXTENSIONS-1:0][3:0] m_mem_byte_en; - word_t [N_EXTENSIONS-1:0] m_mem_load; - word_t [N_EXTENSIONS-1:0] m_mem_store; - logic [N_EXTENSIONS-1:0] m_reg_wdata; - logic [N_EXTENSIONS-1:0] m_reg_w; - - - /****************************************************************** + import rv32i_types_pkg::*; + + parameter int N_EXTENSIONS = `NUM_EXTENSIONS; + localparam int N_EXT_BITS = $clog2(N_EXTENSIONS); + + /****************************************************************** + * Signal Instantiations + ******************************************************************/ + + // Decode Stage Signals + word_t [N_EXTENSIONS-1:0] d_insn; + logic [N_EXTENSIONS-1:0] d_insn_claim; + logic [N_EXTENSIONS-1:0] d_mem_to_reg; // ignored in two stage pipe rmgmt + logic [N_EXTENSIONS-1:0][4:0] d_rsel_s_0; + logic [N_EXTENSIONS-1:0][4:0] d_rsel_s_1; + logic [N_EXTENSIONS-1:0][4:0] d_rsel_d; + + //Execute Stage Signals + logic [N_EXTENSIONS-1:0] e_start; + logic [N_EXTENSIONS-1:0] e_exception; + logic [N_EXTENSIONS-1:0] e_busy; + word_t [N_EXTENSIONS-1:0] e_rdata_s_0; + word_t [N_EXTENSIONS-1:0] e_rdata_s_1; + logic [N_EXTENSIONS-1:0] e_branch_jump; + word_t [N_EXTENSIONS-1:0] e_pc; + word_t [N_EXTENSIONS-1:0] e_br_j_addr; + word_t [N_EXTENSIONS-1:0] e_reg_wdata; + logic [N_EXTENSIONS-1:0] e_reg_w; + + //Memory Stage Signals + logic [N_EXTENSIONS-1:0] m_exception; + logic [N_EXTENSIONS-1:0] m_busy; + word_t [N_EXTENSIONS-1:0] m_mem_addr; + logic [N_EXTENSIONS-1:0] m_mem_ren; + logic [N_EXTENSIONS-1:0] m_mem_wen; + logic [N_EXTENSIONS-1:0] m_mem_busy; + logic [N_EXTENSIONS-1:0][3:0] m_mem_byte_en; + word_t [N_EXTENSIONS-1:0] m_mem_load; + word_t [N_EXTENSIONS-1:0] m_mem_store; + logic [N_EXTENSIONS-1:0] m_reg_wdata; + logic [N_EXTENSIONS-1:0] m_reg_w; + + + /****************************************************************** * Extension connections * - * Modify RISC_MGMT_EXTENSIONS in component_selection_defines.vh + * Modify RISC_MGMT_EXTENSIONS in component_selection_defines.vh * to edit the included instruction extensions * - ******************************************************************/ - - `RISC_MGMT_EXTENSIONS - - - /****************************************************************** - * Begin RISC-MGMT Logic - ******************************************************************/ - - /* Send instruction to extensions */ - assign d_insn = {N_EXTENSIONS{rm_if.insn}}; - - /* Extension Tokens */ - - integer i; - logic [N_EXTENSIONS-1:0] tokens; - logic ext_is_active; - logic [N_EXT_BITS-1:0] active_ext; - - assign tokens = d_insn_claim; - assign ext_is_active = |tokens; - assign rm_if.active_insn = ext_is_active; - - assign rm_if.ex_token = ext_is_active; - - always_comb begin - active_ext = 0; - for(i = 0; i < N_EXTENSIONS; i++) begin - if(tokens[i]) - active_ext = i; + ******************************************************************/ + + `RISC_MGMT_EXTENSIONS + + + /****************************************************************** + * Begin RISC-MGMT Logic + ******************************************************************/ + + /* Send instruction to extensions */ + assign d_insn = {N_EXTENSIONS{rm_if.insn}}; + + /* Extension Tokens */ + + integer i; + logic [N_EXTENSIONS-1:0] tokens; + logic ext_is_active; + logic [N_EXT_BITS-1:0] active_ext; + + assign tokens = d_insn_claim; + assign ext_is_active = |tokens; + assign rm_if.active_insn = ext_is_active; + + assign rm_if.ex_token = ext_is_active; + + always_comb begin + active_ext = 0; + for (i = 0; i < N_EXTENSIONS; i++) begin + if (tokens[i]) active_ext = i; + end end - end - - /* Pipeline Control / Automatic Clock Gating + + /* Pipeline Control / Automatic Clock Gating * Not present in 2 stage pipeline implementation * All pipeline control is handled in standard core automatically * Stalls will be forwarded to the standard core */ - assign rm_if.execute_stall = e_busy[active_ext] && ext_is_active; - assign rm_if.memory_stall = m_busy[active_ext] && ext_is_active; + assign rm_if.execute_stall = e_busy[active_ext] && ext_is_active; + assign rm_if.memory_stall = m_busy[active_ext] && ext_is_active; - // start signal for multicycle execute stages - logic ex_start; + // start signal for multicycle execute stages + logic ex_start; - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - ex_start <= 1'b0; - else if (rm_if.if_ex_enable) - ex_start <= 1'b1; - else - ex_start <= 1'b0; - end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) ex_start <= 1'b0; + else if (rm_if.if_ex_enable) ex_start <= 1'b1; + else ex_start <= 1'b0; + end - assign e_start = {N_EXTENSIONS{ex_start}} & tokens; - assign rm_if.risc_mgmt_start = e_start; // to stall rv32c load buffer + assign e_start = {N_EXTENSIONS{ex_start}} & tokens; + assign rm_if.risc_mgmt_start = e_start; // to stall rv32c load buffer - /* Registerfile / Forwarding Logic + /* Registerfile / Forwarding Logic * Forwarding not present in 2 stage pipeline */ - - // Reg reads and decode - assign rm_if.req_reg_r = ext_is_active; - assign rm_if.rsel_s_0 = d_rsel_s_0[active_ext]; - assign rm_if.rsel_s_1 = d_rsel_s_1[active_ext]; - assign rm_if.rsel_d = d_rsel_d[active_ext]; - assign e_rdata_s_0 = {N_EXTENSIONS{rm_if.rdata_s_0}}; - assign e_rdata_s_1 = {N_EXTENSIONS{rm_if.rdata_s_1}}; - - // Reg Writeback - assign rm_if.req_reg_w = (e_reg_w[active_ext] || m_reg_w[active_ext]) && ext_is_active; - assign rm_if.reg_w = e_reg_w[active_ext] || m_reg_w[active_ext]; - assign rm_if.reg_wdata = e_reg_w[active_ext] ? e_reg_wdata[active_ext] : m_reg_wdata[active_ext]; - - - /* Branch Jump Control */ - - assign rm_if.req_br_j = e_branch_jump[active_ext] && ext_is_active; - assign rm_if.branch_jump = e_branch_jump[active_ext]; - assign rm_if.br_j_addr = e_br_j_addr[active_ext]; - assign e_pc = {N_EXTENSIONS{rm_if.pc}}; - - /* Memory Access Control */ - - assign rm_if.req_mem = (m_mem_ren[active_ext] || m_mem_wen[active_ext]) && ext_is_active; - assign rm_if.mem_addr = m_mem_addr[active_ext]; - assign rm_if.mem_byte_en = m_mem_byte_en[active_ext]; - assign rm_if.mem_store = m_mem_store[active_ext]; - assign rm_if.mem_ren = m_mem_ren[active_ext]; - assign rm_if.mem_wen = m_mem_wen[active_ext]; - assign m_mem_busy = {N_EXTENSIONS{rm_if.mem_busy}}; - assign m_mem_load = {N_EXTENSIONS{rm_if.mem_load}}; - - - /* Exception Reporting */ - assign rm_if.exception = (e_exception[active_ext] || m_exception[active_ext]) && ext_is_active; - assign rm_if.ex_cause = active_ext; - + + // Reg reads and decode + assign rm_if.req_reg_r = ext_is_active; + assign rm_if.rsel_s_0 = d_rsel_s_0[active_ext]; + assign rm_if.rsel_s_1 = d_rsel_s_1[active_ext]; + assign rm_if.rsel_d = d_rsel_d[active_ext]; + assign e_rdata_s_0 = {N_EXTENSIONS{rm_if.rdata_s_0}}; + assign e_rdata_s_1 = {N_EXTENSIONS{rm_if.rdata_s_1}}; + + // Reg Writeback + assign rm_if.req_reg_w = (e_reg_w[active_ext] || m_reg_w[active_ext]) && ext_is_active; + assign rm_if.reg_w = e_reg_w[active_ext] || m_reg_w[active_ext]; + assign rm_if.reg_wdata = e_reg_w[active_ext] ? e_reg_wdata[active_ext] : m_reg_wdata[active_ext]; + + + /* Branch Jump Control */ + + assign rm_if.req_br_j = e_branch_jump[active_ext] && ext_is_active; + assign rm_if.branch_jump = e_branch_jump[active_ext]; + assign rm_if.br_j_addr = e_br_j_addr[active_ext]; + assign e_pc = {N_EXTENSIONS{rm_if.pc}}; + + /* Memory Access Control */ + + assign rm_if.req_mem = (m_mem_ren[active_ext] || m_mem_wen[active_ext]) && ext_is_active; + assign rm_if.mem_addr = m_mem_addr[active_ext]; + assign rm_if.mem_byte_en = m_mem_byte_en[active_ext]; + assign rm_if.mem_store = m_mem_store[active_ext]; + assign rm_if.mem_ren = m_mem_ren[active_ext]; + assign rm_if.mem_wen = m_mem_wen[active_ext]; + assign m_mem_busy = {N_EXTENSIONS{rm_if.mem_busy}}; + assign m_mem_load = {N_EXTENSIONS{rm_if.mem_load}}; + + + /* Exception Reporting */ + assign rm_if.exception = (e_exception[active_ext] || m_exception[active_ext]) && ext_is_active; + assign rm_if.ex_cause = active_ext; + endmodule diff --git a/source_code/risc_mgmt/wscript b/source_code/risc_mgmt/wscript deleted file mode 100644 index 88a3b7fae..000000000 --- a/source_code/risc_mgmt/wscript +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(extensions) - cnf.recurse(tspp) - -def sim_source(cnf): - cnf.recurse(extensions) - cnf.recurse(tspp) diff --git a/source_code/rv32c/decompressor.sv b/source_code/rv32c/decompressor.sv index 95565d0ed..7c60d5607 100644 --- a/source_code/rv32c/decompressor.sv +++ b/source_code/rv32c/decompressor.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,24 +23,24 @@ */ `include "decompressor_if.vh" -module decompressor -( - decompressor_if.dcpr dcpr_if +module decompressor ( + decompressor_if.dcpr dcpr_if ); logic c0_format, c1_format, c2_format; logic upper3_0, upper3_1, upper3_2, upper3_3, upper3_4, upper3_5, upper3_6, upper3_7; logic c_addi, c_addi16sp, c_addi14spn, c_slli, c_andi, c_srli, c_srai; logic c_mv, c_add, c_and, c_or, c_xor, c_sub; - logic c_lw, c_sw, c_lwsp, c_swsp, c_flw, c_fsw, c_flwsp, c_fswsp, c_fld, c_fsd, c_fldsp, c_fsdsp; + logic + c_lw, c_sw, c_lwsp, c_swsp, c_flw, c_fsw, c_flwsp, c_fswsp, c_fld, c_fsd, c_fldsp, c_fsdsp; logic c_j, c_jal, c_jr, c_jalr, c_beqz, c_bnez; logic c_li, c_lui, c_nop, c_ebreak; logic rtype, itype, stype, btype, utype, jtype; - logic [2:0] funct3; - logic [6:0] funct7; - logic [5:0] imm_i_c; + logic [ 2:0] funct3; + logic [ 6:0] funct7; + logic [ 5:0] imm_i_c; logic [11:0] imm_i; logic [10:0] imm_j; - logic [7:0] imm_b; + logic [ 7:0] imm_b; logic [4:0] rd, rs2; logic [4:0] offset_c, offset_c_df; logic [5:0] offset_csp, offset_csp_df; @@ -72,11 +72,16 @@ module decompressor // Register assign c_mv = upper3_4 & c2_format & dcpr_if.inst16[12] == 1'b0 & dcpr_if.inst16[6:2] != 5'd0; - assign c_add = upper3_4 & c2_format & dcpr_if.inst16[12] & dcpr_if.inst16[6:2] != 5'd0 & dcpr_if.inst16[11:7] != 5'd0; - assign c_and = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) & (dcpr_if.inst16[6:5] == 2'd3); - assign c_or = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) & (dcpr_if.inst16[6:5] == 2'd2); - assign c_xor = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) & (dcpr_if.inst16[6:5] == 2'd1); - assign c_sub = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) & (dcpr_if.inst16[6:5] == 2'd0); + assign c_add = upper3_4 & c2_format & dcpr_if.inst16[12] + & dcpr_if.inst16[6:2] != 5'd0 & dcpr_if.inst16[11:7] != 5'd0; + assign c_and = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) + & (dcpr_if.inst16[6:5] == 2'd3); + assign c_or = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) + & (dcpr_if.inst16[6:5] == 2'd2); + assign c_xor = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) + & (dcpr_if.inst16[6:5] == 2'd1); + assign c_sub = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) + & (dcpr_if.inst16[6:5] == 2'd0); // Load/Store assign c_lw = upper3_2 & c0_format; @@ -86,23 +91,25 @@ module decompressor assign c_fld = upper3_1 & c0_format; assign c_fsd = upper3_5 & c0_format; assign c_lwsp = upper3_2 & c2_format; - assign c_swsp = upper3_6 & c2_format; + assign c_swsp = upper3_6 & c2_format; assign c_flwsp = upper3_3 & c2_format; - assign c_fswsp = upper3_7 & c2_format; + assign c_fswsp = upper3_7 & c2_format; assign c_fldsp = upper3_1 & c2_format; - assign c_fsdsp = upper3_5 & c2_format; + assign c_fsdsp = upper3_5 & c2_format; // Control Transfer assign c_j = upper3_5 & c1_format; assign c_jal = upper3_1 & c1_format; assign c_jr = upper3_4 & dcpr_if.inst16[12] == 1'b0 & dcpr_if.inst16[6:2] == 5'd0 & c2_format; - assign c_jalr = upper3_4 & c2_format & dcpr_if.inst16[12] & dcpr_if.inst16[6:2] == 5'd0 & dcpr_if.inst16[11:7] != 5'd0; + assign c_jalr = upper3_4 & c2_format & dcpr_if.inst16[12] + & dcpr_if.inst16[6:2] == 5'd0 & dcpr_if.inst16[11:7] != 5'd0; assign c_beqz = upper3_6 & c1_format; - assign c_bnez = upper3_7 & c1_format; + assign c_bnez = upper3_7 & c1_format; // Constant Generation assign c_li = upper3_2 & c1_format; - assign c_lui = upper3_3 & (dcpr_if.inst16[11:7] != 5'd0) & (dcpr_if.inst16[11:7] != 5'd2) & c1_format; + assign c_lui = upper3_3 & (dcpr_if.inst16[11:7] != 5'd0) + & (dcpr_if.inst16[11:7] != 5'd2) & c1_format; // Others //assign illegal = dcpr_if.inst16 == 16'd0; @@ -111,91 +118,170 @@ module decompressor // Inst type assign rtype = c_mv | c_add | c_and | c_or | c_xor | c_sub; - assign itype = c_addi | c_addi16sp | c_addi14spn | c_slli | c_andi | c_srli | c_lw | c_flw | c_lwsp | c_flwsp | c_li | c_nop | c_srai | c_fld | c_fldsp; + assign itype = c_addi | c_addi16sp | c_addi14spn | c_slli | c_andi | c_srli + | c_lw | c_flw | c_lwsp | c_flwsp | c_li | c_nop | c_srai | c_fld | c_fldsp; assign stype = c_sw | c_swsp | c_fsw | c_fswsp | c_fsd | c_fsdsp; assign btype = c_beqz | c_bnez; assign utype = c_lui; assign jtype = c_j | c_jal | c_jr | c_jalr; assign imm_i_c = {dcpr_if.inst16[12], dcpr_if.inst16[6:2]}; - assign imm_j = {dcpr_if.inst16[12], dcpr_if.inst16[8], dcpr_if.inst16[10:9], dcpr_if.inst16[6], dcpr_if.inst16[7], dcpr_if.inst16[2], dcpr_if.inst16[11], dcpr_if.inst16[5:3]}; - assign imm_b = {dcpr_if.inst16[12], dcpr_if.inst16[6:5], dcpr_if.inst16[2], dcpr_if.inst16[11:10], dcpr_if.inst16[4:3]}; + assign imm_j = { + dcpr_if.inst16[12], + dcpr_if.inst16[8], + dcpr_if.inst16[10:9], + dcpr_if.inst16[6], + dcpr_if.inst16[7], + dcpr_if.inst16[2], + dcpr_if.inst16[11], + dcpr_if.inst16[5:3] + }; + assign imm_b = { + dcpr_if.inst16[12], + dcpr_if.inst16[6:5], + dcpr_if.inst16[2], + dcpr_if.inst16[11:10], + dcpr_if.inst16[4:3] + }; assign offset_c = {dcpr_if.inst16[5], dcpr_if.inst16[12:10], dcpr_if.inst16[6]}; - assign offset_csp = (c_swsp | c_fswsp) ? {dcpr_if.inst16[8:7], dcpr_if.inst16[12:9]} : {dcpr_if.inst16[3:2], dcpr_if.inst16[12], dcpr_if.inst16[6:4]}; + assign offset_csp = (c_swsp | c_fswsp) ? {dcpr_if.inst16[8:7], dcpr_if.inst16[12:9]} + : {dcpr_if.inst16[3:2], dcpr_if.inst16[12], dcpr_if.inst16[6:4]}; assign offset_c_df = {dcpr_if.inst16[6:5], dcpr_if.inst16[12:10]}; - assign offset_csp_df = c_fsdsp ? {dcpr_if.inst16[9:7], dcpr_if.inst16[12:10]} : {dcpr_if.inst16[4:2], dcpr_if.inst16[12], dcpr_if.inst16[6:5]}; + assign offset_csp_df = c_fsdsp ? {dcpr_if.inst16[9:7], dcpr_if.inst16[12:10]} + : {dcpr_if.inst16[4:2], dcpr_if.inst16[12], dcpr_if.inst16[6:5]}; - // Sign Extend immediate + // Sign Extend immediate always_comb begin if (c_srai) imm_i = {7'b0100000, imm_i_c[4:0]}; - else if (c_srli | c_slli) imm_i = {7'b0000000, imm_i_c[4:0]}; - else if (c_addi16sp) imm_i = {{2{dcpr_if.inst16[12]}}, {dcpr_if.inst16[12], dcpr_if.inst16[4:3], dcpr_if.inst16[5], dcpr_if.inst16[2], dcpr_if.inst16[6]}, 4'd0}; - else if (c_addi14spn) imm_i = {2'd0, {dcpr_if.inst16[10:7], dcpr_if.inst16[12], dcpr_if.inst16[11], dcpr_if.inst16[5], dcpr_if.inst16[6]}, 2'd0}; + else if (c_srli | c_slli) imm_i = {7'b0000000, imm_i_c[4:0]}; + else if (c_addi16sp) + imm_i = { + {2{dcpr_if.inst16[12]}}, + { + dcpr_if.inst16[12], + dcpr_if.inst16[4:3], + dcpr_if.inst16[5], + dcpr_if.inst16[2], + dcpr_if.inst16[6] + }, + 4'd0 + }; + else if (c_addi14spn) + imm_i = { + 2'd0, + { + dcpr_if.inst16[10:7], + dcpr_if.inst16[12], + dcpr_if.inst16[11], + dcpr_if.inst16[5], + dcpr_if.inst16[6] + }, + 2'd0 + }; else imm_i = {{6{imm_i_c[5]}}, imm_i_c}; end // Sign Extend offset - assign offset = (c_sw | c_lw | c_fsw | c_flw) ? {5'd0, offset_c, 2'd0} : {4'd0, offset_csp, 2'd0}; + assign offset = (c_sw | c_lw | c_fsw | c_flw) ? {5'd0, offset_c, 2'd0} + : {4'd0, offset_csp, 2'd0}; assign offset_df = (c_fld | c_fsd) ? {4'd0, offset_c_df, 3'd0} : {3'd0, offset_csp_df, 3'd0}; assign jump_offset = {{9{imm_j[10]}}, imm_j, 1'b0}; assign branch_offset = {{4{imm_b[7]}}, imm_b, 1'b0}; // Select funct3 in 32 bit instruction always_comb begin -// if (c_addi | c_addi16sp | c_addi14spn | c_add | c_sub | c_mv | c_li | c_nop | c_beqz) funct3 = 3'b000; - if (c_slli | c_bnez) funct3 = 3'b001; - else if (c_andi | c_and) funct3 = 3'b111; - else if (c_srli | c_srai) funct3 = 3'b101; - else if (c_or) funct3 = 3'b110; - else if (c_xor) funct3 = 3'b100; - else if (c_sw | c_fsw | c_swsp | c_fswsp | c_lw | c_flw | c_lwsp | c_flwsp) funct3 = 3'b010; - else if (c_fld| c_fsd | c_fldsp | c_fsdsp) funct3 = 3'b011; + // if (c_addi | c_addi16sp | c_addi14spn | c_add | c_sub | c_mv | c_li | c_nop | c_beqz) funct3 = 3'b000; + if (c_slli | c_bnez) funct3 = 3'b001; + else if (c_andi | c_and) funct3 = 3'b111; + else if (c_srli | c_srai) funct3 = 3'b101; + else if (c_or) funct3 = 3'b110; + else if (c_xor) funct3 = 3'b100; + else if (c_sw | c_fsw | c_swsp | c_fswsp | c_lw | c_flw | c_lwsp | c_flwsp) funct3 = 3'b010; + else if (c_fld | c_fsd | c_fldsp | c_fsdsp) funct3 = 3'b011; else funct3 = 3'b000; end // Select register - assign rd = (c_srli | c_srai | c_and | c_or | c_xor | c_sub | c_sw | c_lw | c_fsw | c_fsd | c_flw | c_fld | c_beqz | c_bnez | c_andi) ? {2'd1, dcpr_if.inst16[9:7]} : dcpr_if.inst16[11:7]; - assign rs2 = (c_mv | c_add) ? dcpr_if.inst16[6:2] : {2'd1, dcpr_if.inst16[4:2]}; + assign rd = (c_srli | c_srai | c_and | c_or | c_xor | c_sub | c_sw | c_lw | c_fsw | c_fsd | c_flw | c_fld | c_beqz | c_bnez | c_andi) + ? {2'd1, dcpr_if.inst16[9:7]} : dcpr_if.inst16[11:7]; + assign rs2 = (c_mv | c_add) ? dcpr_if.inst16[6:2] : {2'd1, dcpr_if.inst16[4:2]}; // Encode full 32 bit instruction always_comb begin if (rtype) begin - if (c_sub) dcpr_if.inst32 = {7'b0100000, rs2, rd, funct3, rd, 7'b0110011}; - else if (c_mv) dcpr_if.inst32 = {7'b0000000, rs2, 5'd0, funct3, rd, 7'b0110011}; - else dcpr_if.inst32 = {7'b0000000, rs2, rd, funct3, rd, 7'b0110011}; - end else if (itype) begin - if (c_addi16sp) dcpr_if.inst32 = {imm_i, 5'd2, funct3, 5'd2, 7'b0010011}; - else if (c_addi14spn) dcpr_if.inst32 = {imm_i, 5'd2, funct3, rs2, 7'b0010011}; - else if (c_li) dcpr_if.inst32 = {imm_i, 5'd0, funct3, rd, 7'b0010011}; - else if (c_lw) dcpr_if.inst32 = {offset, rd, funct3, rs2, 7'b0000011}; - else if (c_flw) dcpr_if.inst32 = {offset, rd, funct3, rs2, 7'b0000111}; - else if (c_fld) dcpr_if.inst32 = {offset_df, rd, funct3, rs2, 7'b0000111}; - else if (c_lwsp) dcpr_if.inst32 = {offset, 5'd2, funct3, rd, 7'b0000011}; - else if (c_flwsp) dcpr_if.inst32 = {offset, 5'd2, funct3, rd, 7'b0000111}; - else if (c_fldsp) dcpr_if.inst32 = {offset_df, 5'd2, funct3, rd, 7'b0000111}; - else if (c_nop) dcpr_if.inst32 = {17'd0, funct3, 5'd0, 7'b0010011}; - else dcpr_if.inst32 = {imm_i, rd, funct3, rd, 7'b0010011}; + if (c_sub) dcpr_if.inst32 = {7'b0100000, rs2, rd, funct3, rd, 7'b0110011}; + else if (c_mv) dcpr_if.inst32 = {7'b0000000, rs2, 5'd0, funct3, rd, 7'b0110011}; + else dcpr_if.inst32 = {7'b0000000, rs2, rd, funct3, rd, 7'b0110011}; + end else if (itype) begin + if (c_addi16sp) dcpr_if.inst32 = {imm_i, 5'd2, funct3, 5'd2, 7'b0010011}; + else if (c_addi14spn) dcpr_if.inst32 = {imm_i, 5'd2, funct3, rs2, 7'b0010011}; + else if (c_li) dcpr_if.inst32 = {imm_i, 5'd0, funct3, rd, 7'b0010011}; + else if (c_lw) dcpr_if.inst32 = {offset, rd, funct3, rs2, 7'b0000011}; + else if (c_flw) dcpr_if.inst32 = {offset, rd, funct3, rs2, 7'b0000111}; + else if (c_fld) dcpr_if.inst32 = {offset_df, rd, funct3, rs2, 7'b0000111}; + else if (c_lwsp) dcpr_if.inst32 = {offset, 5'd2, funct3, rd, 7'b0000011}; + else if (c_flwsp) dcpr_if.inst32 = {offset, 5'd2, funct3, rd, 7'b0000111}; + else if (c_fldsp) dcpr_if.inst32 = {offset_df, 5'd2, funct3, rd, 7'b0000111}; + else if (c_nop) dcpr_if.inst32 = {17'd0, funct3, 5'd0, 7'b0010011}; + else dcpr_if.inst32 = {imm_i, rd, funct3, rd, 7'b0010011}; end else if (stype) begin - if (c_sw) dcpr_if.inst32 = {offset[11:5], rs2, rd, funct3, offset[4:0], 7'b0100011}; - else if (c_fsw) dcpr_if.inst32 = {offset[11:5], rs2, rd, funct3, offset[4:0], 7'b0100111}; - else if (c_fsd) dcpr_if.inst32 = {offset_df[11:5], rs2, rd, funct3, offset_df[4:0], 7'b0100111}; - else if (c_fswsp) dcpr_if.inst32 = {offset[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset[4:0], 7'b0100111}; - else if (c_fsdsp) dcpr_if.inst32 = {offset_df[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset_df[4:0], 7'b0100111}; - else dcpr_if.inst32 = {offset[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset[4:0], 7'b0100011}; - end else if (btype) dcpr_if.inst32 = {branch_offset[12], branch_offset[10:5], 5'd0, rd, funct3, branch_offset[4:1], branch_offset[11], 7'b1100011}; + if (c_sw) dcpr_if.inst32 = {offset[11:5], rs2, rd, funct3, offset[4:0], 7'b0100011}; + else if (c_fsw) + dcpr_if.inst32 = {offset[11:5], rs2, rd, funct3, offset[4:0], 7'b0100111}; + else if (c_fsd) + dcpr_if.inst32 = {offset_df[11:5], rs2, rd, funct3, offset_df[4:0], 7'b0100111}; + else if (c_fswsp) + dcpr_if.inst32 = { + offset[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset[4:0], 7'b0100111 + }; + else if (c_fsdsp) + dcpr_if.inst32 = { + offset_df[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset_df[4:0], 7'b0100111 + }; + else + dcpr_if.inst32 = { + offset[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset[4:0], 7'b0100011 + }; + end else if (btype) + dcpr_if.inst32 = { + branch_offset[12], + branch_offset[10:5], + 5'd0, + rd, + funct3, + branch_offset[4:1], + branch_offset[11], + 7'b1100011 + }; else if (utype) dcpr_if.inst32 = {{{14{imm_i_c[5]}}, imm_i_c}, rd, 7'b0110111}; else if (jtype) begin - if (c_j) dcpr_if.inst32 = {jump_offset[20], jump_offset[10:1], jump_offset[11], jump_offset[19:12], 5'd0, 7'b1101111}; - else if (c_jal) dcpr_if.inst32 = {jump_offset[20], jump_offset[10:1], jump_offset[11], jump_offset[19:12], 5'd1, 7'b1101111}; - else if (c_jr) dcpr_if.inst32 = {12'd0, rd, 3'd0, 5'd0, 7'b1100111}; - else dcpr_if.inst32 = {12'd0, rd, 3'd0, 5'd1, 7'b1100111}; - end else if (c_ebreak) dcpr_if.inst32 = {12'b000000000001, 5'd0, 3'd0, 5'd0, 7'b1110011}; - else dcpr_if.inst32 = 32'd0; + if (c_j) + dcpr_if.inst32 = { + jump_offset[20], + jump_offset[10:1], + jump_offset[11], + jump_offset[19:12], + 5'd0, + 7'b1101111 + }; + else if (c_jal) + dcpr_if.inst32 = { + jump_offset[20], + jump_offset[10:1], + jump_offset[11], + jump_offset[19:12], + 5'd1, + 7'b1101111 + }; + else if (c_jr) dcpr_if.inst32 = {12'd0, rd, 3'd0, 5'd0, 7'b1100111}; + else dcpr_if.inst32 = {12'd0, rd, 3'd0, 5'd1, 7'b1100111}; + end else if (c_ebreak) dcpr_if.inst32 = {12'b000000000001, 5'd0, 3'd0, 5'd0, 7'b1110011}; + else dcpr_if.inst32 = 32'd0; end -// assign dcpr_if.edit_rd = c_addi14spn | c_lw | c_and | c_sub | c_or | c_xor | c_srli | c_srai | c_andi; -// assign dcpr_if.edit_rs1 = c_lw | c_sw | c_and | c_sub | c_or | c_xor | c_beqz | c_bnez | c_srli | c_srai | c_andi; -// assign dcpr_if.edit_rs2 = c_sw | c_and | c_sub | c_or | c_xor; + // assign dcpr_if.edit_rd = c_addi14spn | c_lw | c_and | c_sub | c_or | c_xor | c_srli | c_srai | c_andi; + // assign dcpr_if.edit_rs1 = c_lw | c_sw | c_and | c_sub | c_or | c_xor | c_beqz | c_bnez | c_srli | c_srai | c_andi; + // assign dcpr_if.edit_rs2 = c_sw | c_and | c_sub | c_or | c_xor; assign dcpr_if.c_ena = dcpr_if.inst16[1:0] != 2'b11; endmodule diff --git a/source_code/rv32c/fetch_buffer.sv b/source_code/rv32c/fetch_buffer.sv index c5cff08d2..4a751cbeb 100644 --- a/source_code/rv32c/fetch_buffer.sv +++ b/source_code/rv32c/fetch_buffer.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,9 +23,9 @@ */ `include "fetch_buffer_if.vh" -module fetch_buffer -( - input logic clk, n_rst, +module fetch_buffer ( + input logic clk, + n_rst, fetch_buffer_if.fb fb_if ); logic [15:0] buffer, nextbuffer; @@ -35,7 +35,7 @@ module fetch_buffer logic reset_next, finished; logic inst_arrived_delay; - always_ff @ (posedge clk, negedge n_rst) begin + always_ff @(posedge clk, negedge n_rst) begin if (n_rst == 0) inst_arrived_delay <= 1'b0; else inst_arrived_delay <= fb_if.inst_arrived; end @@ -45,7 +45,7 @@ module fetch_buffer //assign fb_if.done_earlier = 0; // Buffer and PC logic - always_ff @ (posedge clk, negedge n_rst) begin + always_ff @(posedge clk, negedge n_rst) begin if (n_rst == 0) begin buffer <= 16'd0; combine_reg <= 1'b0; @@ -71,7 +71,7 @@ module fetch_buffer final_inst_store <= final_inst; reset_next <= 1'b0; end - end + end always_comb begin next_imem_pc = fb_if.imem_pc; @@ -81,9 +81,10 @@ module fetch_buffer waitnext = 1'b0; final_inst = final_inst_store; finished = 1'b0; - if (fb_if.inst_arrived & reset_next & (pc != fb_if.imem_pc)) begin // Handle Jump/Branch Condition when misaligned + // Jump/Branch condition when misaligned + if (fb_if.inst_arrived & reset_next & (pc != fb_if.imem_pc)) begin next_imem_pc = fb_if.imem_pc + 4; - if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed final_inst = {16'd0, fb_if.inst[31:16]}; fb_if.nextpc = pc + 2; finished = 1'b1; @@ -94,16 +95,16 @@ module fetch_buffer finished = 1'b0; end end else if (fb_if.reset_en) begin - next_imem_pc = {fb_if.reset_pc[31:2], 2'b0}; // Always aligned - fb_if.nextpc = fb_if.reset_pc; // Can be misaligned - nextbuffer = 16'd0; - final_inst = 32'd0; + next_imem_pc = {fb_if.reset_pc[31:2], 2'b0}; // Always aligned + fb_if.nextpc = fb_if.reset_pc; // Can be misaligned + nextbuffer = 16'd0; + final_inst = 32'd0; end else if (fb_if.inst_arrived & combine_reg) begin final_inst = {fb_if.inst[15:0], buffer}; nextbuffer = fb_if.inst[31:16]; fb_if.nextpc = pc + 4; finished = 1'b1; - if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed waitnext = 1; next_imem_pc = fb_if.imem_pc; end else begin @@ -115,12 +116,12 @@ module fetch_buffer finished = 1'b1; fb_if.nextpc = pc + 2; next_imem_pc = fb_if.imem_pc + 4; - end else if (fb_if.inst[1:0] != 2'b11) begin // lower 16 bits are compressed + end else if (fb_if.inst[1:0] != 2'b11) begin // lower 16 bits are compressed final_inst = fb_if.inst[15:0]; nextbuffer = fb_if.inst[31:16]; fb_if.nextpc = pc + 2; finished = 1'b1; - if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed waitnext = 1; next_imem_pc = fb_if.imem_pc; end else begin @@ -139,6 +140,6 @@ module fetch_buffer assign fb_if.result = final_inst; //assign debug = fb_if.inst_arrived ? final_inst : final_inst_store; //assign c_ena = fb_if.result[1:0] != 2'b11; - assign fb_if.done = fb_if.inst_arrived & finished; + assign fb_if.done = fb_if.inst_arrived & finished; endmodule diff --git a/source_code/rv32c/rv32c.core b/source_code/rv32c/rv32c.core new file mode 100644 index 000000000..ede7e0416 --- /dev/null +++ b/source_code/rv32c/rv32c.core @@ -0,0 +1,18 @@ +CAPI=2: +name: socet:riscv:rv32c:0.1.0 +description: rv32c + +filesets: + rtl: + files: + - decompressor.sv + - rv32c_disabled.sv + - rv32c_enabled.sv + - fetch_buffer.sv + - rv32c_wrapper.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl \ No newline at end of file diff --git a/source_code/rv32c/rv32c_disabled.sv b/source_code/rv32c/rv32c_disabled.sv index 0ec15d1b0..7ff190932 100644 --- a/source_code/rv32c/rv32c_disabled.sv +++ b/source_code/rv32c/rv32c_disabled.sv @@ -2,21 +2,21 @@ `include "fetch_buffer_if.vh" `include "decompressor_if.vh" -module rv32c_disabled -( - input logic clk, nrst, - rv32c_if.rv32c rv32cif - ); +module rv32c_disabled ( + input logic clk, + nrst, + rv32c_if.rv32c rv32cif +); - assign rv32cif.done = '0; - assign rv32cif.done_earlier = '0; - assign rv32cif.nextpc = '0; - assign rv32cif.imem_pc = '0; - assign rv32cif.result = '0; - assign rv32cif.inst32 = '0; - assign rv32cif.c_ena = '0; + assign rv32cif.done = '0; + assign rv32cif.done_earlier = '0; + assign rv32cif.nextpc = '0; + assign rv32cif.imem_pc = '0; + assign rv32cif.result = '0; + assign rv32cif.inst32 = '0; + assign rv32cif.c_ena = '0; - assign rv32cif.rv32c_ena = '0; + assign rv32cif.rv32c_ena = '0; endmodule diff --git a/source_code/rv32c/rv32c_enabled.sv b/source_code/rv32c/rv32c_enabled.sv index 49fd221d7..78a8cf100 100644 --- a/source_code/rv32c/rv32c_enabled.sv +++ b/source_code/rv32c/rv32c_enabled.sv @@ -2,41 +2,40 @@ `include "fetch_buffer_if.vh" `include "decompressor_if.vh" -module rv32c_enabled -( - input logic clk, nrst, - rv32c_if.rv32c rv32cif - ); - //parameter RESET_PC = 32'h80000000; - // Fetch Buffer - fetch_buffer_if fb_if(); - fetch_buffer BUFFER - ( - .clk(clk), - .n_rst(nrst), - .fb_if(fb_if) - ); - assign fb_if.inst = rv32cif.inst; - assign fb_if.reset_en = rv32cif.reset_en; - assign fb_if.reset_pc = rv32cif.reset_pc; - assign fb_if.inst_arrived = rv32cif.inst_arrived; - assign fb_if.pc_update = rv32cif.pc_update; - assign fb_if.ex_busy = rv32cif.ex_busy; - assign fb_if.reset_pc_val = rv32cif.reset_pc_val; - assign rv32cif.done = fb_if.done; - assign rv32cif.done_earlier = fb_if.done_earlier & (rv32cif.halt == 0); - ///assign rv32cif.done_earlier_send = fb_if.done_earlier_send; - assign rv32cif.nextpc = fb_if.nextpc; - assign rv32cif.imem_pc = fb_if.imem_pc; - assign rv32cif.result = fb_if.result; +module rv32c_enabled ( + input logic clk, + nrst, + rv32c_if.rv32c rv32cif +); + //parameter RESET_PC = 32'h80000000; + // Fetch Buffer + fetch_buffer_if fb_if (); + fetch_buffer BUFFER ( + .clk (clk), + .n_rst(nrst), + .fb_if(fb_if) + ); + assign fb_if.inst = rv32cif.inst; + assign fb_if.reset_en = rv32cif.reset_en; + assign fb_if.reset_pc = rv32cif.reset_pc; + assign fb_if.inst_arrived = rv32cif.inst_arrived; + assign fb_if.pc_update = rv32cif.pc_update; + assign fb_if.ex_busy = rv32cif.ex_busy; + assign fb_if.reset_pc_val = rv32cif.reset_pc_val; + assign rv32cif.done = fb_if.done; + assign rv32cif.done_earlier = fb_if.done_earlier & (rv32cif.halt == 0); + ///assign rv32cif.done_earlier_send = fb_if.done_earlier_send; + assign rv32cif.nextpc = fb_if.nextpc; + assign rv32cif.imem_pc = fb_if.imem_pc; + assign rv32cif.result = fb_if.result; - // Decompressor - decompressor_if dcpr_if(); - decompressor DECOMPRESSOR (dcpr_if); - assign dcpr_if.inst16 = rv32cif.inst16; - assign rv32cif.inst32 = dcpr_if.inst32; - assign rv32cif.c_ena = dcpr_if.c_ena; + // Decompressor + decompressor_if dcpr_if (); + decompressor DECOMPRESSOR (dcpr_if); + assign dcpr_if.inst16 = rv32cif.inst16; + assign rv32cif.inst32 = dcpr_if.inst32; + assign rv32cif.c_ena = dcpr_if.c_ena; - assign rv32cif.rv32c_ena = 1; + assign rv32cif.rv32c_ena = 1; endmodule diff --git a/source_code/rv32c/rv32c_wrapper.sv b/source_code/rv32c/rv32c_wrapper.sv index f0b4997f8..9277bed27 100644 --- a/source_code/rv32c/rv32c_wrapper.sv +++ b/source_code/rv32c/rv32c_wrapper.sv @@ -1,18 +1,19 @@ `include "rv32c_if.vh" `include "component_selection_defines.vh" -module rv32c_wrapper( - input logic CLK, nRST, - rv32c_if.rv32c rv32cif +module rv32c_wrapper ( + input logic CLK, + nRST, + rv32c_if.rv32c rv32cif ); -generate - /* verilator lint_off width */ - case(RV32C_ENABLED) - /* verilator lint_on width */ - "disabled" : rv32c_disabled RV32C(.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); - "enabled" : rv32c_enabled RV32C(.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); - endcase -endgenerate + generate + /* verilator lint_off width */ + case (RV32C_ENABLED) + /* verilator lint_on width */ + "disabled": rv32c_disabled RV32C (.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); + "enabled": rv32c_enabled RV32C (.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); + endcase + endgenerate endmodule diff --git a/source_code/sparce/sparce_disabled/sparce_disabled.sv b/source_code/sparce/sparce_disabled/sparce_disabled.sv index 1a6127f93..564a9dcd4 100644 --- a/source_code/sparce/sparce_disabled/sparce_disabled.sv +++ b/source_code/sparce/sparce_disabled/sparce_disabled.sv @@ -1,12 +1,12 @@ /* * Copyright 2019 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,25 +16,26 @@ * * Filename: sparce_disabled.sv * -* Created by: Vadim V. Nikiforov +* Created by: Vadim V. Nikiforov * Email: vnikifor@purdue.edu * Date Created: 04/29/2019 * Description: The top level file for a CPU without sparsity optimizations -* enabled +* enabled */ `include "sparce_pipeline_if.vh" -module sparce_disabled( - input logic CLK, nRST, - sparce_pipeline_if.sparce sparce_if +module sparce_disabled ( + input logic CLK, + nRST, + sparce_pipeline_if.sparce sparce_if ); - // when disabled, sparce should never force the pipeline to skip + // when disabled, sparce should never force the pipeline to skip - // all inputs are to be ignored - assign sparce_if.skipping = 1'b0; - assign sparce_if.sparce_target = '0; + // all inputs are to be ignored + assign sparce_if.skipping = 1'b0; + assign sparce_if.sparce_target = '0; endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_cfid.sv b/source_code/sparce/sparce_enabled/sparce_cfid.sv index 8c2fffe0a..dedebea4e 100644 --- a/source_code/sparce/sparce_enabled/sparce_cfid.sv +++ b/source_code/sparce/sparce_enabled/sparce_cfid.sv @@ -1,12 +1,12 @@ /* * Copyright 2019 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,22 +25,22 @@ // modport cfid ( // output ctrl_flow_enable, // input rdata -// ); +// ); `include "sparce_internal_if.vh" -module sparce_cfid(sparce_internal_if.cfid cfid_if); - import rv32i_types_pkg::*; +module sparce_cfid ( + sparce_internal_if.cfid cfid_if +); + import rv32i_types_pkg::*; - opcode_t cf_op; - assign cf_op = opcode_t'(cfid_if.rdata[OP_W-1:0]); + opcode_t cf_op; + assign cf_op = opcode_t'(cfid_if.rdata[OP_W-1:0]); - always_comb begin - if (cf_op == BRANCH || cf_op == JAL || cf_op == JALR) - cfid_if.ctrl_flow_enable = 0; - else - cfid_if.ctrl_flow_enable = 1; - end + always_comb begin + if (cf_op == BRANCH || cf_op == JAL || cf_op == JALR) cfid_if.ctrl_flow_enable = 0; + else cfid_if.ctrl_flow_enable = 1; + end endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_enabled.sv b/source_code/sparce/sparce_enabled/sparce_enabled.sv index b80321d5d..b2e80dd19 100644 --- a/source_code/sparce/sparce_enabled/sparce_enabled.sv +++ b/source_code/sparce/sparce_enabled/sparce_enabled.sv @@ -1,12 +1,12 @@ /* * Copyright 2019 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,7 +16,7 @@ * * Filename: sparce_enabled.sv * -* Created by: Vadim V. Nikiforov +* Created by: Vadim V. Nikiforov * Email: vnikifor@purdue.edu * Date Created: 04/29/2019 * Description: The top level file for sparsity enabled for the CPU @@ -25,34 +25,45 @@ `include "sparce_pipeline_if.vh" `include "sparce_internal_if.vh" -module sparce_enabled( - input logic CLK, nRST, - sparce_pipeline_if.sparce sparce_if +module sparce_enabled ( + input logic CLK, + nRST, + sparce_pipeline_if.sparce sparce_if ); - sparce_internal_if internal_if(); - - // assign inputs to the internal modules - assign internal_if.pc = sparce_if.pc; - assign internal_if.wb_data = sparce_if.wb_data; - assign internal_if.wb_en = sparce_if.wb_en; - assign internal_if.sasa_data = sparce_if.sasa_data; - assign internal_if.sasa_addr = sparce_if.sasa_addr; - assign internal_if.sasa_wen = sparce_if.sasa_wen; - assign internal_if.rd = sparce_if.rd; - assign internal_if.sasa_enable = sparce_if.if_ex_enable; - assign internal_if.rdata = sparce_if.rdata; - - // assign to sparce module outputs - assign sparce_if.skipping = internal_if.skipping; - assign sparce_if.sparce_target = internal_if.sparce_target; - - // instantiate internal modules - sparce_svc sparce_svc_i(internal_if.svc); - sparce_sprf sparce_sprf_i(CLK, nRST, internal_if.sprf); - sparce_sasa_table #(.SASA_SETS(4)) sparce_sasa_table_i (CLK, nRST, internal_if.sasa_table); - sparce_psru sparce_psru_i(internal_if.psru); - sparce_cfid sparce_cfid_i(internal_if.cfid); + sparce_internal_if internal_if (); + + // assign inputs to the internal modules + assign internal_if.pc = sparce_if.pc; + assign internal_if.wb_data = sparce_if.wb_data; + assign internal_if.wb_en = sparce_if.wb_en; + assign internal_if.sasa_data = sparce_if.sasa_data; + assign internal_if.sasa_addr = sparce_if.sasa_addr; + assign internal_if.sasa_wen = sparce_if.sasa_wen; + assign internal_if.rd = sparce_if.rd; + assign internal_if.sasa_enable = sparce_if.if_ex_enable; + assign internal_if.rdata = sparce_if.rdata; + + // assign to sparce module outputs + assign sparce_if.skipping = internal_if.skipping; + assign sparce_if.sparce_target = internal_if.sparce_target; + + // instantiate internal modules + sparce_svc sparce_svc_i (internal_if.svc); + sparce_sprf sparce_sprf_i ( + CLK, + nRST, + internal_if.sprf + ); + sparce_sasa_table #( + .SASA_SETS(4) + ) sparce_sasa_table_i ( + CLK, + nRST, + internal_if.sasa_table + ); + sparce_psru sparce_psru_i (internal_if.psru); + sparce_cfid sparce_cfid_i (internal_if.cfid); endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_psru.sv b/source_code/sparce/sparce_enabled/sparce_psru.sv index c1b778fba..79c62f6b3 100644 --- a/source_code/sparce/sparce_enabled/sparce_psru.sv +++ b/source_code/sparce/sparce_enabled/sparce_psru.sv @@ -1,13 +1,13 @@ /* * Copyright 2019 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,7 +17,7 @@ * * Filename: sparce_psru.sv * -* Created by: Vadim V. Nikiforov +* Created by: Vadim V. Nikiforov * Email: vnikifor@purdue.edu * Date Created: 04/14/2019 * Description: The file containing pre-identify and skip redundancy unit @@ -30,23 +30,25 @@ // input valid, insts_to_skip, preceding_pc, condition, rs1_sparsity, rs2_sparsity, ctrl_flow_enable // ); -module sparce_psru(sparce_internal_if.psru psru_if); +module sparce_psru ( + sparce_internal_if.psru psru_if +); - always_comb begin - if (psru_if.valid) begin - // choose the correct condition to evaluate - if(psru_if.condition == SASA_COND_OR) begin - psru_if.skipping = (psru_if.rs1_sparsity || psru_if.rs2_sparsity) && psru_if.ctrl_flow_enable; - end else begin - psru_if.skipping = (psru_if.rs1_sparsity && psru_if.rs2_sparsity) && psru_if.ctrl_flow_enable; - end - // calculate the new program counter - psru_if.sparce_target = psru_if.preceding_pc + (psru_if.insts_to_skip << 2) + 4; - end else begin - // don't skip if the SASA table entry is invalid - psru_if.skipping = 1'b0; - psru_if.sparce_target = '1; + always_comb begin + if (psru_if.valid) begin + // choose the correct condition to evaluate + if (psru_if.condition == SASA_COND_OR) begin + psru_if.skipping = (psru_if.rs1_sparsity || psru_if.rs2_sparsity) && psru_if.ctrl_flow_enable; + end else begin + psru_if.skipping = (psru_if.rs1_sparsity && psru_if.rs2_sparsity) && psru_if.ctrl_flow_enable; + end + // calculate the new program counter + psru_if.sparce_target = psru_if.preceding_pc + (psru_if.insts_to_skip << 2) + 4; + end else begin + // don't skip if the SASA table entry is invalid + psru_if.skipping = 1'b0; + psru_if.sparce_target = '1; + end end - end endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_sasa_table.sv b/source_code/sparce/sparce_enabled/sparce_sasa_table.sv index ad6d2a82f..39b6b2944 100644 --- a/source_code/sparce/sparce_enabled/sparce_sasa_table.sv +++ b/source_code/sparce/sparce_enabled/sparce_sasa_table.sv @@ -1,12 +1,12 @@ /* * Copyright 2019 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,7 +16,7 @@ * * Filename: sparce_sasa_table.sv * -* Created by: Vadim V. Nikiforov +* Created by: Vadim V. Nikiforov * Email: vnikifor@purdue.edu * Date Created: 04/6/2019 * Description: The file containing the SASA table @@ -82,7 +82,7 @@ module sparce_sasa_table #(parameter SASA_ENTRIES = 16, parameter SASA_SETS = 1, logic [SASA_SETS:0] sasa_hits; logic sasa_config, sasa_config_match; - + // sasa table configuration register assign sasa_config_match = sasa_if.sasa_enable && sasa_if.sasa_wen && (sasa_if.sasa_addr == SASA_ADDR + 4); @@ -96,7 +96,7 @@ module sparce_sasa_table #(parameter SASA_ENTRIES = 16, parameter SASA_SETS = 1, sasa_config <= sasa_if.sasa_data; end end - end + end // wiring for indexing of the cache arrays assign input_idx = (SASA_ENTRIES == SASA_SETS) ? 0 : input_data.prev_pc; @@ -104,7 +104,7 @@ module sparce_sasa_table #(parameter SASA_ENTRIES = 16, parameter SASA_SETS = 1, assign pc_tag = sasa_if.pc >> (`CLOG2(SASA_ENTRIES/SASA_SETS) + 2); assign sasa_match = sasa_if.sasa_enable && sasa_if.sasa_wen && (sasa_if.sasa_addr == SASA_ADDR); - + always_comb begin : sasa_input_conversion input_data.prev_pc = sasa_if.sasa_data[31:16]; input_data.rs1 = sasa_if.sasa_data[15:11]; diff --git a/source_code/sparce/sparce_enabled/sparce_sprf.sv b/source_code/sparce/sparce_enabled/sparce_sprf.sv index 5c3696623..7949c6f7c 100644 --- a/source_code/sparce/sparce_enabled/sparce_sprf.sv +++ b/source_code/sparce/sparce_enabled/sparce_sprf.sv @@ -1,12 +1,12 @@ /* * Copyright 2019 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,7 +16,7 @@ * * Filename: sparce_sprf.sv * -* Created by: Vadim V. Nikiforov +* Created by: Vadim V. Nikiforov * Email: vnikifor@purdue.edu * Date Created: 04/4/2019 * Description: The file containing the sparsity register file (SpRF) @@ -25,49 +25,53 @@ `include "sparce_internal_if.vh" -module sparce_sprf(input logic CLK, nRST, sparce_internal_if.sprf sprf_if); +module sparce_sprf ( + input logic CLK, + nRST, + sparce_internal_if.sprf sprf_if +); - logic [31:1] sparsity_reg; - logic [31:0] sparsity_out; + logic [31:1] sparsity_reg; + logic [31:0] sparsity_out; - // all register outputs are tied to the registers, except for - // register[0] which is always 1 - assign sparsity_out[0] = 1'b1; - assign sparsity_out[31:1] = sparsity_reg[31:1]; + // all register outputs are tied to the registers, except for + // register[0] which is always 1 + assign sparsity_out[0] = 1'b1; + assign sparsity_out[31:1] = sparsity_reg[31:1]; - // register update logic - always_ff @(posedge CLK, negedge nRST) begin - if(!nRST) begin - // even through regs are initialized to 0, don't treat - // uninitialized registers as sparse registers - sparsity_reg[31:1] <= '0; - end else begin - // keep registers as old values when not modified - sparsity_reg[31:1] <= sparsity_reg[31:1]; - // modify the chosen register if the pipeline is writing back - if(sprf_if.wb_en) begin - sparsity_reg[sprf_if.rd] <= sprf_if.is_sparse; - end + // register update logic + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) begin + // even through regs are initialized to 0, don't treat + // uninitialized registers as sparse registers + sparsity_reg[31:1] <= '0; + end else begin + // keep registers as old values when not modified + sparsity_reg[31:1] <= sparsity_reg[31:1]; + // modify the chosen register if the pipeline is writing back + if (sprf_if.wb_en) begin + sparsity_reg[sprf_if.rd] <= sprf_if.is_sparse; + end + end end - end - // register output logic - always_comb begin - if (sprf_if.sasa_rs1 == '0) begin - sprf_if.rs1_sparsity = 1'b1; - end else if (sprf_if.sasa_rs1 == sprf_if.rd && sprf_if.wb_en) begin - sprf_if.rs1_sparsity = sprf_if.is_sparse; - end else begin - sprf_if.rs1_sparsity = sparsity_out[sprf_if.sasa_rs1]; - end - if (sprf_if.sasa_rs2 == '0) begin - sprf_if.rs2_sparsity = 1'b1; - end else if (sprf_if.sasa_rs2 == sprf_if.rd && sprf_if.wb_en) begin - sprf_if.rs2_sparsity = sprf_if.is_sparse; - end else begin - sprf_if.rs2_sparsity = sparsity_out[sprf_if.sasa_rs2]; + // register output logic + always_comb begin + if (sprf_if.sasa_rs1 == '0) begin + sprf_if.rs1_sparsity = 1'b1; + end else if (sprf_if.sasa_rs1 == sprf_if.rd && sprf_if.wb_en) begin + sprf_if.rs1_sparsity = sprf_if.is_sparse; + end else begin + sprf_if.rs1_sparsity = sparsity_out[sprf_if.sasa_rs1]; + end + if (sprf_if.sasa_rs2 == '0) begin + sprf_if.rs2_sparsity = 1'b1; + end else if (sprf_if.sasa_rs2 == sprf_if.rd && sprf_if.wb_en) begin + sprf_if.rs2_sparsity = sprf_if.is_sparse; + end else begin + sprf_if.rs2_sparsity = sparsity_out[sprf_if.sasa_rs2]; + end end - end endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_svc.sv b/source_code/sparce/sparce_enabled/sparce_svc.sv index 50cf7fe44..8811f6035 100644 --- a/source_code/sparce/sparce_enabled/sparce_svc.sv +++ b/source_code/sparce/sparce_enabled/sparce_svc.sv @@ -1,12 +1,12 @@ /* * Copyright 2019 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,7 +16,7 @@ * * Filename: sparce_svc.sv * -* Created by: Vadim V. Nikiforov +* Created by: Vadim V. Nikiforov * Email: vnikifor@purdue.edu * Date Created: 04/4/2019 * Description: The file containing the sparsity value chekcer (SVC) @@ -25,10 +25,12 @@ `include "sparce_internal_if.vh" -module sparce_svc(sparce_internal_if.svc svc_if); +module sparce_svc ( + sparce_internal_if.svc svc_if +); - // sparsity only when the writeback data is zero - assign svc_if.is_sparse = (svc_if.wb_data == 0); + // sparsity only when the writeback data is zero + assign svc_if.is_sparse = (svc_if.wb_data == 0); endmodule diff --git a/source_code/sparce/sparce_wrapper.sv b/source_code/sparce/sparce_wrapper.sv index 8d82f4159..8916861b0 100644 --- a/source_code/sparce/sparce_wrapper.sv +++ b/source_code/sparce/sparce_wrapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2019 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,28 +16,29 @@ * * Filename: sparce_wrapper.sv * -* Created by: Vadim V. Nikiforov +* Created by: Vadim V. Nikiforov * Email: vnikifor@purdue.edu * Date Created: 04/17/2019 -* Description: The top level wrapper file for implementations of +* Description: The top level wrapper file for implementations of * sparisity optimiations based on the SparCE paper */ `include "sparce_pipeline_if.vh" `include "component_selection_defines.vh" -module sparce_wrapper( - input logic CLK, nRST, - sparce_pipeline_if.sparce sparce_if +module sparce_wrapper ( + input logic CLK, + nRST, + sparce_pipeline_if.sparce sparce_if ); - // Sparsity blocks used based on the SPARCE_ENABLED definition - generate - case (SPARCE_ENABLED) - "disabled" : sparce_disabled sparce(.*); - "enabled" : sparce_enabled sparce(.*); - endcase - endgenerate + // Sparsity blocks used based on the SPARCE_ENABLED definition + generate + case (SPARCE_ENABLED) + "disabled": sparce_disabled sparce (.*); + "enabled": sparce_enabled sparce (.*); + endcase + endgenerate endmodule diff --git a/source_code/sparce/wscript b/source_code/sparce/wscript deleted file mode 100644 index 8eb0fdef4..000000000 --- a/source_code/sparce/wscript +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(tspp) - -def sim_source(cnf): - cnf.recurse(tspp) diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index 50519b349..17085272d 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -1,21 +1,21 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: RISCVBusiness.sv -* +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 @@ -23,7 +23,6 @@ */ `include "generic_bus_if.vh" -`include "ahb_if.vh" `include "component_selection_defines.vh" `include "risc_mgmt_if.vh" `include "cache_control_if.vh" @@ -34,38 +33,43 @@ `include "rv32c_if.vh" module RISCVBusiness ( - input logic CLK, nRST, - output logic wfi, halt, - core_interrupt_if.core interrupt_if, - `ifdef BUS_INTERFACE_GENERIC_BUS - generic_bus_if.cpu gen_bus_if - `elsif BUS_INTERFACE_AHB - ahb_if.ahb_m ahb_master - `endif + input logic CLK, + nRST, + output logic wfi, + halt, + core_interrupt_if.core interrupt_if, +`ifdef BUS_INTERFACE_GENERIC_BUS + generic_bus_if.cpu gen_bus_if +`elsif BUS_INTERFACE_AHB + ahb_if.manager ahb_master +`elsif BUS_INTERFACE_APB + apb_if.requester apb_requester +`endif + ); - parameter RESET_PC = 32'h80000000; + parameter logic [31:0] RESET_PC = 32'h80000000; - // Interface instantiations + // Interface instantiations - generic_bus_if tspp_icache_gen_bus_if(); - generic_bus_if tspp_dcache_gen_bus_if(); - generic_bus_if icache_mc_if(); - generic_bus_if dcache_mc_if(); - generic_bus_if pipeline_trans_if(); - risc_mgmt_if rm_if(); - predictor_pipeline_if predict_if(); - prv_pipeline_if prv_pipe_if(); - cache_control_if cc_if(); - sparce_pipeline_if sparce_if(); - rv32c_if rv32cif(); + generic_bus_if tspp_icache_gen_bus_if (); + generic_bus_if tspp_dcache_gen_bus_if (); + generic_bus_if icache_mc_if (); + generic_bus_if dcache_mc_if (); + generic_bus_if pipeline_trans_if (); + risc_mgmt_if rm_if (); + predictor_pipeline_if predict_if (); + prv_pipeline_if prv_pipe_if (); + cache_control_if cc_if (); + sparce_pipeline_if sparce_if (); + rv32c_if rv32cif (); - //interface instantiations - tspp_fetch_execute_if fetch_ex_if(); - tspp_hazard_unit_if hazard_if(); + //interface instantiations + tspp_fetch_execute_if fetch_ex_if (); + tspp_hazard_unit_if hazard_if (); - // Module Instantiations -/* + // Module Instantiations + /* pipeline_wrapper pipeline ( .CLK(CLK), .nRST(nRST), @@ -80,63 +84,63 @@ module RISCVBusiness ( ); */ - tspp_fetch_stage #( - .RESET_PC(RESET_PC) - ) fetch_stage_i ( - .CLK(CLK), - .nRST(nRST), - .fetch_ex_if(fetch_ex_if), - .hazard_if(hazard_if), - .predict_if(predict_if), - .igen_bus_if(tspp_icache_gen_bus_if), - .sparce_if(sparce_if), - .rv32cif(rv32cif) - ); - - tspp_execute_stage execute_stage_i ( - .CLK(CLK), - .nRST(nRST), - .fetch_ex_if(fetch_ex_if), - .hazard_if(hazard_if), - .predict_if(predict_if), - .dgen_bus_if(tspp_dcache_gen_bus_if), - .prv_pipe_if(prv_pipe_if), - .halt(halt), - .rm_if(rm_if), - .cc_if(cc_if), - .sparce_if(sparce_if), - .rv32cif(rv32cif), - .wfi(wfi) - ); - - tspp_hazard_unit hazard_unit_i ( - .hazard_if(hazard_if), - .prv_pipe_if(prv_pipe_if), - .rm_if(rm_if), - .sparce_if(sparce_if) - ); - - - branch_predictor_wrapper branch_predictor_i ( - .CLK(CLK), - .nRST(nRST), - .predict_if(predict_if) - ); - - priv_wrapper priv_wrapper_i ( - .CLK(CLK), - .nRST(nRST), - .prv_pipe_if(prv_pipe_if), - .interrupt_if - ); - - risc_mgmt_wrapper rmgmt ( - .CLK(CLK), - .nRST(nRST), - .rm_if(rm_if) - ); - -/* + tspp_fetch_stage #( + .RESET_PC(RESET_PC) + ) fetch_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .igen_bus_if(tspp_icache_gen_bus_if), + .sparce_if(sparce_if), + .rv32cif(rv32cif) + ); + + tspp_execute_stage execute_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .dgen_bus_if(tspp_dcache_gen_bus_if), + .prv_pipe_if(prv_pipe_if), + .halt(halt), + .rm_if(rm_if), + .cc_if(cc_if), + .sparce_if(sparce_if), + .rv32cif(rv32cif), + .wfi(wfi) + ); + + tspp_hazard_unit hazard_unit_i ( + .hazard_if(hazard_if), + .prv_pipe_if(prv_pipe_if), + .rm_if(rm_if), + .sparce_if(sparce_if) + ); + + + branch_predictor_wrapper branch_predictor_i ( + .CLK(CLK), + .nRST(nRST), + .predict_if(predict_if) + ); + + priv_wrapper priv_wrapper_i ( + .CLK(CLK), + .nRST(nRST), + .prv_pipe_if(prv_pipe_if), + .interrupt_if + ); + + risc_mgmt_wrapper rmgmt ( + .CLK (CLK), + .nRST (nRST), + .rm_if(rm_if) + ); + + /* caches_wrapper caches ( .CLK(CLK), .nRST(nRST), @@ -148,58 +152,73 @@ module RISCVBusiness ( ); */ - separate_caches sep_caches ( - .CLK(CLK), - .nRST(nRST), - .icache_proc_gen_bus_if(tspp_icache_gen_bus_if), - .icache_mem_gen_bus_if(icache_mc_if), - .dcache_proc_gen_bus_if(tspp_dcache_gen_bus_if), - .dcache_mem_gen_bus_if(dcache_mc_if), - .cc_if(cc_if) - ); - - memory_controller mc ( - .CLK(CLK), - .nRST(nRST), - .d_gen_bus_if(dcache_mc_if), - .i_gen_bus_if(icache_mc_if), - .out_gen_bus_if(pipeline_trans_if) - ); - - sparce_wrapper sparce_wrapper_i ( - .CLK(CLK), - .nRST(nRST), - .sparce_if(sparce_if) - ); - - rv32c_wrapper rv32c ( - .CLK(CLK), - .nRST(nRST), - .rv32cif(rv32cif) - ); - - // Instantiate the chosen bus interface - - generate - case (BUS_INTERFACE_TYPE) - "generic_bus_if" : begin - generic_nonpipeline bt( - .CLK(CLK), - .nRST(nRST), - .pipeline_trans_if(pipeline_trans_if), - .out_gen_bus_if(gen_bus_if) - ); - end - "ahb_if" : begin - ahb bt ( - .CLK(CLK), - .nRST(nRST), - .out_gen_bus_if(pipeline_trans_if), - .ahb_m(ahb_master) - ); - end - endcase - - endgenerate + separate_caches sep_caches ( + .CLK(CLK), + .nRST(nRST), + .icache_proc_gen_bus_if(tspp_icache_gen_bus_if), + .icache_mem_gen_bus_if(icache_mc_if), + .dcache_proc_gen_bus_if(tspp_dcache_gen_bus_if), + .dcache_mem_gen_bus_if(dcache_mc_if), + .cc_if(cc_if) + ); + + memory_controller mc ( + .CLK(CLK), + .nRST(nRST), + .d_gen_bus_if(dcache_mc_if), + .i_gen_bus_if(icache_mc_if), + .out_gen_bus_if(pipeline_trans_if) + ); + + /* + sparce_wrapper sparce_wrapper_i ( + .CLK(CLK), + .nRST(nRST), + .sparce_if(sparce_if) + );*/ + + sparce_disabled sparce_disabled_i ( + .CLK(CLK), + .nRST(nRST), + .sparce_if(sparce_if) + ); + + rv32c_wrapper rv32c ( + .CLK(CLK), + .nRST(nRST), + .rv32cif(rv32cif) + ); + + // Instantiate the chosen bus interface + + generate + case (BUS_INTERFACE_TYPE) + "generic_bus_if": begin : g_generic_bus_if + generic_nonpipeline bt ( + .CLK(CLK), + .nRST(nRST), + .pipeline_trans_if(pipeline_trans_if), + .out_gen_bus_if(gen_bus_if) + ); + end + "ahb_if": begin : g_ahb_if + ahb bt ( + .CLK(CLK), + .nRST(nRST), + .out_gen_bus_if(pipeline_trans_if), + .ahb_m(ahb_master) + ); + end + "apb_if": begin : g_apb_if + apb bt( + .CLK(CLK), + .nRST(nRST), + .out_gen_bus_if(pipeline_trans_if), + .apbif(apb_requester) + ); + end + endcase + + endgenerate endmodule diff --git a/source_code/standard_core/alu.sv b/source_code/standard_core/alu.sv index 5f5be44fc..8af0f4b1d 100644 --- a/source_code/standard_core/alu.sv +++ b/source_code/standard_core/alu.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,54 +27,53 @@ `include "alu_if.vh" module alu ( - alu_if.alu alu_if + alu_if.alu alu_if ); - - import alu_types_pkg::*; - import rv32i_types_pkg::*; - word_t adder_result, op_a, op_b, twos_comp_b; - logic carry_out, sign_r, sign_a, sign_b; - logic [WORD_SIZE : 0] adder_out, op_a_ext, op_b_ext; + import alu_types_pkg::*; + import rv32i_types_pkg::*; + + word_t adder_result, op_a, op_b, twos_comp_b; + logic carry_out, sign_r, sign_a, sign_b; + logic [WORD_SIZE : 0] adder_out, op_a_ext, op_b_ext; - //sign bits of adder result and operands - assign sign_r = adder_out[WORD_SIZE-1]; - assign sign_a = alu_if.port_a[WORD_SIZE-1]; - assign sign_b = alu_if.port_b[WORD_SIZE-1]; + //sign bits of adder result and operands + assign sign_r = adder_out[WORD_SIZE-1]; + assign sign_a = alu_if.port_a[WORD_SIZE-1]; + assign sign_b = alu_if.port_b[WORD_SIZE-1]; - //assign adder operands (2's compilment b for subtraction) - assign op_a = alu_if.port_a; - assign op_b = (alu_if.aluop == ALU_ADD) ? alu_if.port_b : twos_comp_b; - assign twos_comp_b = (~alu_if.port_b) + 1; + //assign adder operands (2's compilment b for subtraction) + assign op_a = alu_if.port_a; + assign op_b = (alu_if.aluop == ALU_ADD) ? alu_if.port_b : twos_comp_b; + assign twos_comp_b = (~alu_if.port_b) + 1; - //extend operands a and b for the adder - assign op_a_ext[WORD_SIZE] = (alu_if.aluop == ALU_SLTU) ? 1'b0 : op_a[WORD_SIZE-1]; - assign op_b_ext[WORD_SIZE] = (alu_if.aluop == ALU_SLTU) ? 1'b0 : op_b[WORD_SIZE-1]; - assign op_a_ext[WORD_SIZE-1:0] = op_a; - assign op_b_ext[WORD_SIZE-1:0] = op_b; + //extend operands a and b for the adder + assign op_a_ext[WORD_SIZE] = (alu_if.aluop == ALU_SLTU) ? 1'b0 : op_a[WORD_SIZE-1]; + assign op_b_ext[WORD_SIZE] = (alu_if.aluop == ALU_SLTU) ? 1'b0 : op_b[WORD_SIZE-1]; + assign op_a_ext[WORD_SIZE-1:0] = op_a; + assign op_b_ext[WORD_SIZE-1:0] = op_b; - //separate the carry out and result - assign adder_out = op_a_ext + op_b_ext; - assign adder_result = adder_out[WORD_SIZE-1:0]; - assign carry_out = adder_out[WORD_SIZE]; + //separate the carry out and result + assign adder_out = op_a_ext + op_b_ext; + assign adder_result = adder_out[WORD_SIZE-1:0]; + assign carry_out = adder_out[WORD_SIZE]; - always_comb begin - casez (alu_if.aluop) - ALU_SLL : alu_if.port_out = alu_if.port_a << alu_if.port_b[4:0]; - ALU_SRL : alu_if.port_out = alu_if.port_a >> alu_if.port_b[4:0]; - ALU_SRA : alu_if.port_out = $signed(alu_if.port_a) >>> alu_if.port_b[4:0]; - ALU_AND : alu_if.port_out = alu_if.port_a & alu_if.port_b; - ALU_OR : alu_if.port_out = alu_if.port_a | alu_if.port_b; - ALU_XOR : alu_if.port_out = alu_if.port_a ^ alu_if.port_b; - ALU_SLT : begin - alu_if.port_out = (sign_a & !sign_b) ? 1 : - ((!sign_a & sign_b) ? 0 : sign_r); - end - ALU_SLTU : alu_if.port_out = ~carry_out & |(op_b_ext); - ALU_ADD : alu_if.port_out = adder_result; - ALU_SUB : alu_if.port_out = adder_result; - default : alu_if.port_out = '0; - endcase - end + always_comb begin + casez (alu_if.aluop) + ALU_SLL: alu_if.port_out = alu_if.port_a << alu_if.port_b[4:0]; + ALU_SRL: alu_if.port_out = alu_if.port_a >> alu_if.port_b[4:0]; + ALU_SRA: alu_if.port_out = $signed(alu_if.port_a) >>> alu_if.port_b[4:0]; + ALU_AND: alu_if.port_out = alu_if.port_a & alu_if.port_b; + ALU_OR: alu_if.port_out = alu_if.port_a | alu_if.port_b; + ALU_XOR: alu_if.port_out = alu_if.port_a ^ alu_if.port_b; + ALU_SLT: begin + alu_if.port_out = (sign_a & !sign_b) ? 1 : ((!sign_a & sign_b) ? 0 : sign_r); + end + ALU_SLTU: alu_if.port_out = ~carry_out & |(op_b_ext); + ALU_ADD: alu_if.port_out = adder_result; + ALU_SUB: alu_if.port_out = adder_result; + default: alu_if.port_out = '0; + endcase + end endmodule diff --git a/source_code/standard_core/branch_res.sv b/source_code/standard_core/branch_res.sv index f04f66e5d..a8459c132 100644 --- a/source_code/standard_core/branch_res.sv +++ b/source_code/standard_core/branch_res.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,67 +20,66 @@ * Email: jskubic@purdue.edu * Date Created: 06/14/2016 * Description: Determines if a branch should be taken and outputs -* the target address. Optimized to use only one adder +* the target address. Optimized to use only one adder */ `include "branch_res_if.vh" module branch_res ( - branch_res_if.bres br_if + branch_res_if.bres br_if ); - - import rv32i_types_pkg::*; - - word_t offset; - logic lt, eq, ltu; - logic sign_1, sign_2, sign_r, carry_out; - logic [WORD_SIZE : 0] adder_out, op_1_ext, op_2_ext; - // target addr generation - assign offset = $signed(br_if.imm_sb); - assign br_if.branch_addr = br_if.pc + offset; + import rv32i_types_pkg::*; - //sign bits - assign sign_1 = br_if.rs1_data[WORD_SIZE-1]; - assign sign_2 = br_if.rs2_data[WORD_SIZE-1]; - assign sign_r = adder_out[WORD_SIZE-1]; - - //build operands - assign op_1_ext[WORD_SIZE-1:0] = br_if.rs1_data; - assign op_2_ext[WORD_SIZE-1:0] = (~br_if.rs2_data) + 1; + word_t offset; + logic lt, eq, ltu; + logic sign_1, sign_2, sign_r, carry_out; + logic [WORD_SIZE : 0] + adder_out, op_1_ext /* verilator split_var */, op_2_ext /* verilator split_var */; - always_comb begin - if(br_if.branch_type == BLTU || br_if.branch_type == BGEU) begin - op_1_ext[WORD_SIZE] = 1'b0; - op_2_ext[WORD_SIZE] = 1'b0; - end - else begin - op_1_ext[WORD_SIZE] = op_1_ext[WORD_SIZE-1]; - op_2_ext[WORD_SIZE] = op_2_ext[WORD_SIZE-1]; + // target addr generation + assign offset = $signed(br_if.imm_sb); + assign br_if.branch_addr = br_if.pc + offset; + + //sign bits + assign sign_1 = br_if.rs1_data[WORD_SIZE-1]; + assign sign_2 = br_if.rs2_data[WORD_SIZE-1]; + assign sign_r = adder_out[WORD_SIZE-1]; + + //build operands + assign op_1_ext[WORD_SIZE-1:0] = br_if.rs1_data; + assign op_2_ext[WORD_SIZE-1:0] = (~br_if.rs2_data) + 1; + + always_comb begin + if (br_if.branch_type == BLTU || br_if.branch_type == BGEU) begin + op_1_ext[WORD_SIZE] = 1'b0; + op_2_ext[WORD_SIZE] = 1'b0; + end else begin + op_1_ext[WORD_SIZE] = op_1_ext[WORD_SIZE-1]; + op_2_ext[WORD_SIZE] = op_2_ext[WORD_SIZE-1]; + end end - end - //adder - assign adder_out = op_1_ext + op_2_ext; - assign carry_out = adder_out[WORD_SIZE]; - - // condition calculations - assign eq = br_if.rs1_data == br_if.rs2_data; - assign lt = (sign_1 & ~sign_2) ? 1 : - ((~sign_1 & sign_2) ? 0 : sign_r); + //adder + assign adder_out = op_1_ext + op_2_ext; + assign carry_out = adder_out[WORD_SIZE]; - assign ltu = ~carry_out & |(op_2_ext); + // condition calculations + assign eq = br_if.rs1_data == br_if.rs2_data; + assign lt = (sign_1 & ~sign_2) ? 1 : ((~sign_1 & sign_2) ? 0 : sign_r); - always_comb begin - casez (br_if.branch_type) - BEQ : br_if.branch_taken = eq; - BNE : br_if.branch_taken = ~eq; - BLT : br_if.branch_taken = lt; - BGE : br_if.branch_taken = ~lt; - BLTU : br_if.branch_taken = ltu; - BGEU : br_if.branch_taken = ~ltu; - default : br_if.branch_taken = 1'b0; - endcase - end + assign ltu = ~carry_out & |(op_2_ext); + + always_comb begin + casez (br_if.branch_type) + BEQ : br_if.branch_taken = eq; + BNE : br_if.branch_taken = ~eq; + BLT : br_if.branch_taken = lt; + BGE : br_if.branch_taken = ~lt; + BLTU : br_if.branch_taken = ltu; + BGEU : br_if.branch_taken = ~ltu; + default : br_if.branch_taken = 1'b0; + endcase + end endmodule diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index c053e3bc9..ceb94a445 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -28,237 +28,221 @@ `include "risc_mgmt_if.vh" `include "decompressor_if.vh" -module control_unit -( - control_unit_if.control_unit cu_if, - rv32i_reg_file_if.cu rf_if, - input logic [4:0] rmgmt_rsel_s_0, rmgmt_rsel_s_1, rmgmt_rsel_d, - input logic rmgmt_req_reg_r, rmgmt_req_reg_w +module control_unit ( + control_unit_if.control_unit cu_if, + rv32i_reg_file_if.cu rf_if, + input logic [4:0] rmgmt_rsel_s_0, + rmgmt_rsel_s_1, + rmgmt_rsel_d, + input logic rmgmt_req_reg_r, + rmgmt_req_reg_w ); - import alu_types_pkg::*; - import rv32i_types_pkg::*; - import machine_mode_types_1_12_pkg::*; - - stype_t instr_s; - itype_t instr_i; - rtype_t instr_r; - sbtype_t instr_sb; - utype_t instr_u; - ujtype_t instr_uj; - - assign instr_s = stype_t'(cu_if.instr); - assign instr_i = itype_t'(cu_if.instr); - assign instr_r = rtype_t'(cu_if.instr); - assign instr_sb = sbtype_t'(cu_if.instr); - assign instr_u = utype_t'(cu_if.instr); - assign instr_uj = ujtype_t'(cu_if.instr); - - assign cu_if.opcode = opcode_t'(cu_if.instr[6:0]); - assign rf_if.rs1 = rmgmt_req_reg_r ? rmgmt_rsel_s_0 : cu_if.instr[19:15]; - assign rf_if.rs2 = rmgmt_req_reg_r ? rmgmt_rsel_s_1 : cu_if.instr[24:20]; - assign rf_if.rd = rmgmt_req_reg_w ? rmgmt_rsel_d : cu_if.instr[11:7]; - assign cu_if.shamt = cu_if.instr[24:20]; - - // Assign the immediate values - assign cu_if.imm_I = instr_i.imm11_00; - assign cu_if.imm_S = {instr_s.imm11_05, instr_s.imm04_00}; - assign cu_if.imm_SB = {instr_sb.imm12, instr_sb.imm11, instr_sb.imm10_05, - instr_sb.imm04_01, 1'b0}; - assign cu_if.imm_UJ = {instr_uj.imm20, instr_uj.imm19_12, instr_uj.imm11, - instr_uj.imm10_01, 1'b0}; - assign cu_if.imm_U = {instr_u.imm31_12, 12'b0}; - - assign cu_if.imm_shamt_sel = (cu_if.opcode == IMMED && + import alu_types_pkg::*; + import rv32i_types_pkg::*; + import machine_mode_types_1_12_pkg::*; + + stype_t instr_s; + itype_t instr_i; + rtype_t instr_r; + sbtype_t instr_sb; + utype_t instr_u; + ujtype_t instr_uj; + + assign instr_s = stype_t'(cu_if.instr); + assign instr_i = itype_t'(cu_if.instr); + assign instr_r = rtype_t'(cu_if.instr); + assign instr_sb = sbtype_t'(cu_if.instr); + assign instr_u = utype_t'(cu_if.instr); + assign instr_uj = ujtype_t'(cu_if.instr); + + assign cu_if.opcode = opcode_t'(cu_if.instr[6:0]); + assign rf_if.rs1 = rmgmt_req_reg_r ? rmgmt_rsel_s_0 : cu_if.instr[19:15]; + assign rf_if.rs2 = rmgmt_req_reg_r ? rmgmt_rsel_s_1 : cu_if.instr[24:20]; + assign rf_if.rd = rmgmt_req_reg_w ? rmgmt_rsel_d : cu_if.instr[11:7]; + assign cu_if.shamt = cu_if.instr[24:20]; + + // Assign the immediate values + assign cu_if.imm_I = instr_i.imm11_00; + assign cu_if.imm_S = {instr_s.imm11_05, instr_s.imm04_00}; + assign cu_if.imm_SB = { + instr_sb.imm12, instr_sb.imm11, instr_sb.imm10_05, instr_sb.imm04_01, 1'b0 + }; + assign cu_if.imm_UJ = { + instr_uj.imm20, instr_uj.imm19_12, instr_uj.imm11, instr_uj.imm10_01, 1'b0 + }; + assign cu_if.imm_U = {instr_u.imm31_12, 12'b0}; + + assign cu_if.imm_shamt_sel = (cu_if.opcode == IMMED && (instr_i.funct3 == SLLI || instr_i.funct3 == SRI)); - // Assign branch and load type - assign cu_if.load_type = load_t'(instr_i.funct3); - assign cu_if.branch_type = branch_t'(instr_sb.funct3); - - // Assign memory read/write enables - assign cu_if.dwen = (cu_if.opcode == STORE); - assign cu_if.dren = (cu_if.opcode == LOAD); - assign cu_if.ifence = (cu_if.opcode == MISCMEM) && (rv32i_miscmem_t'(instr_r.funct3) == FENCEI); - - // Assign control flow signals - assign cu_if.branch = (cu_if.opcode == BRANCH); - assign cu_if.jump = (cu_if.opcode == JAL || cu_if.opcode == JALR); - assign cu_if.ex_pc_sel = (cu_if.opcode == JAL || cu_if.opcode == JALR); - assign cu_if.j_sel = (cu_if.opcode == JAL); - // Assign alu operands - always_comb begin - case(cu_if.opcode) - REGREG, IMMED, LOAD : cu_if.alu_a_sel = 2'd0; - STORE : cu_if.alu_a_sel = 2'd1; - AUIPC : cu_if.alu_a_sel = 2'd2; - default : cu_if.alu_a_sel = 2'd2; - endcase - end + // Assign branch and load type + assign cu_if.load_type = load_t'(instr_i.funct3); + assign cu_if.branch_type = branch_t'(instr_sb.funct3); + + // Assign memory read/write enables + assign cu_if.dwen = (cu_if.opcode == STORE); + assign cu_if.dren = (cu_if.opcode == LOAD); + assign cu_if.ifence = (cu_if.opcode == MISCMEM) && (rv32i_miscmem_t'(instr_r.funct3) == FENCEI); + + // Assign control flow signals + assign cu_if.branch = (cu_if.opcode == BRANCH); + assign cu_if.jump = (cu_if.opcode == JAL || cu_if.opcode == JALR); + assign cu_if.ex_pc_sel = (cu_if.opcode == JAL || cu_if.opcode == JALR); + assign cu_if.j_sel = (cu_if.opcode == JAL); + // Assign alu operands + always_comb begin + case (cu_if.opcode) + REGREG, IMMED, LOAD: cu_if.alu_a_sel = 2'd0; + STORE: cu_if.alu_a_sel = 2'd1; + AUIPC: cu_if.alu_a_sel = 2'd2; + default: cu_if.alu_a_sel = 2'd2; + endcase + end - always_comb begin - case(cu_if.opcode) - STORE : cu_if.alu_b_sel = 2'd0; - REGREG : cu_if.alu_b_sel = 2'd1; - IMMED, LOAD : cu_if.alu_b_sel = 2'd2; - AUIPC : cu_if.alu_b_sel = 2'd3; - default : cu_if.alu_b_sel = 2'd1; - endcase - end + always_comb begin + case (cu_if.opcode) + STORE: cu_if.alu_b_sel = 2'd0; + REGREG: cu_if.alu_b_sel = 2'd1; + IMMED, LOAD: cu_if.alu_b_sel = 2'd2; + AUIPC: cu_if.alu_b_sel = 2'd3; + default: cu_if.alu_b_sel = 2'd1; + endcase + end - // Assign write select - always_comb begin - case(cu_if.opcode) - LOAD : cu_if.w_sel = 3'd0; - JAL, JALR : cu_if.w_sel = 3'd1; - LUI : cu_if.w_sel = 3'd2; - IMMED, AUIPC, REGREG : cu_if.w_sel = 3'd3; - SYSTEM : cu_if.w_sel = 3'd4; - default : cu_if.w_sel = 3'd0; - endcase - end + // Assign write select + always_comb begin + case (cu_if.opcode) + LOAD: cu_if.w_sel = 3'd0; + JAL, JALR: cu_if.w_sel = 3'd1; + LUI: cu_if.w_sel = 3'd2; + IMMED, AUIPC, REGREG: cu_if.w_sel = 3'd3; + SYSTEM: cu_if.w_sel = 3'd4; + default: cu_if.w_sel = 3'd0; + endcase + end - // Assign register write enable - always_comb begin - case(cu_if.opcode) - STORE, BRANCH : cu_if.wen = 1'b0; - IMMED, LUI, AUIPC, - REGREG, JAL, JALR, - LOAD : cu_if.wen = 1'b1; - SYSTEM : cu_if.wen = cu_if.csr_rw_valid; - default: cu_if.wen = 1'b0; - endcase - end + // Assign register write enable + always_comb begin + case (cu_if.opcode) + STORE, BRANCH: cu_if.wen = 1'b0; + IMMED, LUI, AUIPC, REGREG, JAL, JALR, LOAD: cu_if.wen = 1'b1; + SYSTEM: cu_if.wen = cu_if.csr_rw_valid; + default: cu_if.wen = 1'b0; + endcase + end - // Assign alu opcode - logic sr, aluop_srl, aluop_sra, aluop_add, aluop_sub, aluop_and, aluop_or; - logic aluop_sll, aluop_xor, aluop_slt, aluop_sltu, add_sub; + // Assign alu opcode + logic sr, aluop_srl, aluop_sra, aluop_add, aluop_sub, aluop_and, aluop_or; + logic aluop_sll, aluop_xor, aluop_slt, aluop_sltu, add_sub; - assign sr = ((cu_if.opcode == IMMED && instr_i.funct3 == SRI) || + assign sr = ((cu_if.opcode == IMMED && instr_i.funct3 == SRI) || (cu_if.opcode == REGREG && instr_r.funct3 == SR)); - assign add_sub = (cu_if.opcode == REGREG && instr_r.funct3 == ADDSUB); + assign add_sub = (cu_if.opcode == REGREG && instr_r.funct3 == ADDSUB); - assign aluop_sll = ((cu_if.opcode == IMMED && instr_i.funct3 == SLLI) || + assign aluop_sll = ((cu_if.opcode == IMMED && instr_i.funct3 == SLLI) || (cu_if.opcode == REGREG && instr_r.funct3 == SLL)); - assign aluop_sra = sr && cu_if.instr[30]; - assign aluop_srl = sr && ~cu_if.instr[30]; - assign aluop_add = ((cu_if.opcode == IMMED && instr_i.funct3 == ADDI) || + assign aluop_sra = sr && cu_if.instr[30]; + assign aluop_srl = sr && ~cu_if.instr[30]; + assign aluop_add = ((cu_if.opcode == IMMED && instr_i.funct3 == ADDI) || (cu_if.opcode == AUIPC) || (add_sub && ~cu_if.instr[30]) || (cu_if.opcode == LOAD) || (cu_if.opcode == STORE)); - assign aluop_sub = (add_sub && cu_if.instr[30]); - assign aluop_and = ((cu_if.opcode == IMMED && instr_i.funct3 == ANDI) || + assign aluop_sub = (add_sub && cu_if.instr[30]); + assign aluop_and = ((cu_if.opcode == IMMED && instr_i.funct3 == ANDI) || (cu_if.opcode == REGREG && instr_r.funct3 == AND)); - assign aluop_or = ((cu_if.opcode == IMMED && instr_i.funct3 == ORI) || + assign aluop_or = ((cu_if.opcode == IMMED && instr_i.funct3 == ORI) || (cu_if.opcode == REGREG && instr_r.funct3 == OR)); - assign aluop_xor = ((cu_if.opcode == IMMED && instr_i.funct3 == XORI) || + assign aluop_xor = ((cu_if.opcode == IMMED && instr_i.funct3 == XORI) || (cu_if.opcode == REGREG && instr_r.funct3 == XOR)); - assign aluop_slt = ((cu_if.opcode == IMMED && instr_i.funct3 == SLTI) || + assign aluop_slt = ((cu_if.opcode == IMMED && instr_i.funct3 == SLTI) || (cu_if.opcode == REGREG && instr_r.funct3 == SLT)); - assign aluop_sltu = ((cu_if.opcode == IMMED && instr_i.funct3 == SLTIU) || + assign aluop_sltu = ((cu_if.opcode == IMMED && instr_i.funct3 == SLTIU) || (cu_if.opcode == REGREG && instr_r.funct3 == SLTU)); - always_comb begin - if (aluop_sll) - cu_if.alu_op = ALU_SLL; - else if (aluop_sra) - cu_if.alu_op = ALU_SRA; - else if (aluop_srl) - cu_if.alu_op = ALU_SRL; - else if (aluop_add) - cu_if.alu_op = ALU_ADD; - else if (aluop_sub) - cu_if.alu_op = ALU_SUB; - else if (aluop_and) - cu_if.alu_op = ALU_AND; - else if (aluop_or) - cu_if.alu_op = ALU_OR; - else if (aluop_xor) - cu_if.alu_op = ALU_XOR; - else if (aluop_slt) - cu_if.alu_op = ALU_SLT; - else if (aluop_sltu) - cu_if.alu_op = ALU_SLTU; - else - cu_if.alu_op = ALU_ADD; - end - - // HALT HACK. Just looking for j + 0x0 (infinite loop) - // Halt required for unit testing, but not useful in tapeout context - // Due to presence of interrupts, infinite loops are valid - generate - if(INFINITE_LOOP_HALTS == "true") begin - assign cu_if.halt = (cu_if.instr == 32'h0000006f); - end else begin - assign cu_if.halt = '0; + always_comb begin + if (aluop_sll) cu_if.alu_op = ALU_SLL; + else if (aluop_sra) cu_if.alu_op = ALU_SRA; + else if (aluop_srl) cu_if.alu_op = ALU_SRL; + else if (aluop_add) cu_if.alu_op = ALU_ADD; + else if (aluop_sub) cu_if.alu_op = ALU_SUB; + else if (aluop_and) cu_if.alu_op = ALU_AND; + else if (aluop_or) cu_if.alu_op = ALU_OR; + else if (aluop_xor) cu_if.alu_op = ALU_XOR; + else if (aluop_slt) cu_if.alu_op = ALU_SLT; + else if (aluop_sltu) cu_if.alu_op = ALU_SLTU; + else cu_if.alu_op = ALU_ADD; end - endgenerate - // Privilege Control Signals - assign cu_if.fault_insn = '0; - always_comb begin - case(cu_if.opcode) - REGREG: cu_if.illegal_insn = instr_r.funct7[0]; - LUI, AUIPC, JAL, JALR, - BRANCH, LOAD, STORE, - IMMED, SYSTEM, - MISCMEM, opcode_t'('0) : cu_if.illegal_insn = 1'b0; - default : cu_if.illegal_insn = 1'b1; - endcase - end - - //Decoding of System Priv Instructions - always_comb begin - cu_if.ret_insn = 1'b0; - cu_if.breakpoint = 1'b0; - cu_if.ecall_insn = 1'b0; - cu_if.wfi = 1'b0; - - if (cu_if.opcode == SYSTEM) begin - if (rv32i_system_t'(instr_i.funct3) == PRIV) begin - if (priv_insn_t'(instr_i.imm11_00) == MRET) - cu_if.ret_insn = 1'b1; - if (priv_insn_t'(instr_i.imm11_00) == EBREAK) - cu_if.breakpoint = 1'b1; - if (priv_insn_t'(instr_i.imm11_00) == ECALL) - cu_if.ecall_insn = 1'b1; - if (priv_insn_t'(instr_i.imm11_00) == WFI) - cu_if.wfi = 1'b1; - end + // HALT HACK. Just looking for j + 0x0 (infinite loop) + // Halt required for unit testing, but not useful in tapeout context + // Due to presence of interrupts, infinite loops are valid + generate + if (INFINITE_LOOP_HALTS == "true") begin : g_inf_loop_halt + assign cu_if.halt = (cu_if.instr == 32'h0000006f); + end else begin : g_no_halt + assign cu_if.halt = '0; + end + endgenerate + // Privilege Control Signals + assign cu_if.fault_insn = '0; + + always_comb begin + case (cu_if.opcode) + REGREG: cu_if.illegal_insn = instr_r.funct7[0]; + LUI, AUIPC, JAL, JALR, BRANCH, LOAD, STORE, IMMED, SYSTEM, MISCMEM, opcode_t'('0): + cu_if.illegal_insn = 1'b0; + default: cu_if.illegal_insn = 1'b1; + endcase end - end - //CSR Insns - always_comb begin - cu_if.csr_swap = 1'b0; - cu_if.csr_clr = 1'b0; - cu_if.csr_set = 1'b0; - cu_if.csr_imm = 1'b0; + //Decoding of System Priv Instructions + always_comb begin + cu_if.ret_insn = 1'b0; + cu_if.breakpoint = 1'b0; + cu_if.ecall_insn = 1'b0; + cu_if.wfi = 1'b0; + + if (cu_if.opcode == SYSTEM) begin + if (rv32i_system_t'(instr_i.funct3) == PRIV) begin + if (priv_insn_t'(instr_i.imm11_00) == MRET) cu_if.ret_insn = 1'b1; + if (priv_insn_t'(instr_i.imm11_00) == EBREAK) cu_if.breakpoint = 1'b1; + if (priv_insn_t'(instr_i.imm11_00) == ECALL) cu_if.ecall_insn = 1'b1; + if (priv_insn_t'(instr_i.imm11_00) == WFI) cu_if.wfi = 1'b1; + end + end + end - if (cu_if.opcode == SYSTEM) begin - if (rv32i_system_t'(instr_r.funct3) == CSRRW) begin - cu_if.csr_swap = 1'b1; - end else - if (rv32i_system_t'(instr_r.funct3) == CSRRS) begin - cu_if.csr_set = 1'b1; - end else if (rv32i_system_t'(instr_r.funct3) == CSRRC) begin - cu_if.csr_clr = 1'b1; - end else if (rv32i_system_t'(instr_r.funct3) == CSRRWI) begin - cu_if.csr_swap = 1'b1; - cu_if.csr_imm = 1'b1; - end else if (rv32i_system_t'(instr_r.funct3) == CSRRSI) begin - cu_if.csr_set = 1'b1; - cu_if.csr_imm = 1'b1; - end else if (rv32i_system_t'(instr_r.funct3) == CSRRCI) begin - cu_if.csr_clr = 1'b1; - cu_if.csr_imm = 1'b1; - end + //CSR Insns + always_comb begin + cu_if.csr_swap = 1'b0; + cu_if.csr_clr = 1'b0; + cu_if.csr_set = 1'b0; + cu_if.csr_imm = 1'b0; + + if (cu_if.opcode == SYSTEM) begin + if (rv32i_system_t'(instr_r.funct3) == CSRRW) begin + cu_if.csr_swap = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRS) begin + cu_if.csr_set = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRC) begin + cu_if.csr_clr = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRWI) begin + cu_if.csr_swap = 1'b1; + cu_if.csr_imm = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRSI) begin + cu_if.csr_set = 1'b1; + cu_if.csr_imm = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRCI) begin + cu_if.csr_clr = 1'b1; + cu_if.csr_imm = 1'b1; + end + end end - end - assign cu_if.csr_rw_valid = (cu_if.csr_swap | cu_if.csr_set | cu_if.csr_clr); + assign cu_if.csr_rw_valid = (cu_if.csr_swap | cu_if.csr_set | cu_if.csr_clr); - assign cu_if.csr_addr = csr_addr_t'(instr_i.imm11_00); - assign cu_if.zimm = cu_if.instr[19:15]; + assign cu_if.csr_addr = csr_addr_t'(instr_i.imm11_00); + assign cu_if.zimm = cu_if.instr[19:15]; endmodule diff --git a/source_code/standard_core/dmem_extender.sv b/source_code/standard_core/dmem_extender.sv index dc84cc07a..c9c07970c 100644 --- a/source_code/standard_core/dmem_extender.sv +++ b/source_code/standard_core/dmem_extender.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,20 +20,20 @@ * Email: jskubic@purdue.edu * Date Created: 06/16/2016 * Description: Swaps the endianess and bit slices the data based on the -* load type +* load type */ -import rv32i_types_pkg::*; module dmem_extender ( - input word_t dmem_in, - input load_t load_type, - input logic [3:0] byte_en, - output word_t ext_out + input rv32i_types_pkg::word_t dmem_in, + input rv32i_types_pkg::load_t load_type, + input logic [3:0] byte_en, + output rv32i_types_pkg::word_t ext_out ); -/* + import rv32i_types_pkg::*; + /* always_comb begin - casez (load_type) + casez (load_type) LB : begin casez (byte_en) 4'b0001 : ext_out = 32'(signed'(dmem_in[7:0])); @@ -80,48 +80,48 @@ module dmem_extender ( - always_comb begin - casez (load_type) - LB : begin - casez (byte_en) - 4'b0001 : ext_out = $signed(dmem_in[7:0]); - 4'b0010 : ext_out = $signed(dmem_in[15:8]); - 4'b0100 : ext_out = $signed(dmem_in[23:16]); - 4'b1000 : ext_out = $signed(dmem_in[31:24]); - default : ext_out = '0; + always_comb begin + casez (load_type) + LB: begin + casez (byte_en) + 4'b0001: ext_out = $signed(dmem_in[7:0]); + 4'b0010: ext_out = $signed(dmem_in[15:8]); + 4'b0100: ext_out = $signed(dmem_in[23:16]); + 4'b1000: ext_out = $signed(dmem_in[31:24]); + default: ext_out = '0; + endcase + end + + LBU: begin + casez (byte_en) + 4'b0001: ext_out = dmem_in[7:0]; + 4'b0010: ext_out = dmem_in[15:8]; + 4'b0100: ext_out = dmem_in[23:16]; + 4'b1000: ext_out = dmem_in[31:24]; + default: ext_out = '0; + endcase + end + + LH: begin + casez (byte_en) + 4'b0011: ext_out = $signed(dmem_in[15:0]); + 4'b1100: ext_out = $signed(dmem_in[31:16]); + default: ext_out = '0; + endcase + end + + LHU: begin + casez (byte_en) + 4'b0011: ext_out = dmem_in[15:0]; + 4'b1100: ext_out = dmem_in[31:16]; + default: ext_out = '0; + endcase + end + + LW: ext_out = dmem_in; + + default: ext_out = '0; endcase - end - - LBU : begin - casez (byte_en) - 4'b0001 : ext_out = dmem_in[7:0]; - 4'b0010 : ext_out = dmem_in[15:8]; - 4'b0100 : ext_out = dmem_in[23:16]; - 4'b1000 : ext_out = dmem_in[31:24]; - default : ext_out = '0; - endcase - end - - LH : begin - casez (byte_en) - 4'b0011 : ext_out = $signed(dmem_in[15:0]); - 4'b1100 : ext_out = $signed(dmem_in[31:16]); - default : ext_out = '0; - endcase - end - - LHU : begin - casez (byte_en) - 4'b0011 : ext_out = dmem_in[15:0]; - 4'b1100 : ext_out = dmem_in[31:16]; - default : ext_out = '0; - endcase - end - - LW : ext_out = dmem_in; - - default : ext_out = '0; - endcase - end + end endmodule diff --git a/source_code/standard_core/endian_swapper.sv b/source_code/standard_core/endian_swapper.sv index c4658ada9..80f0c19d4 100644 --- a/source_code/standard_core/endian_swapper.sv +++ b/source_code/standard_core/endian_swapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,20 +22,21 @@ * Description: Swaps the endianess of the input word */ -import rv32i_types_pkg::*; -module endian_swapper # ( - parameter N_BYTES = WORD_SIZE/8, - parameter N_BITS = N_BYTES*8 +module endian_swapper #( + parameter int N_BYTES = rv32i_types_pkg::WORD_SIZE / 8, + parameter int N_BITS = N_BYTES * 8 ) ( - input [N_BITS-1:0] word_in, - output [N_BITS-1:0] word_out + input [N_BITS-1:0] word_in, + output [N_BITS-1:0] word_out ); - generate - genvar i; - for(i=0; i < N_BYTES; i++) begin : word_assign - assign word_out[N_BITS - (8*i) - 1 : N_BITS - (8 * (i+1))] = word_in[((i+1)*8)-1:(i*8)]; - end : word_assign - endgenerate + import rv32i_types_pkg::*; + + generate + genvar i; + for (i = 0; i < N_BYTES; i++) begin : g_word_assign + assign word_out[N_BITS-(8*i)-1 : N_BITS-(8*(i+1))] = word_in[((i+1)*8)-1:(i*8)]; + end + endgenerate endmodule diff --git a/source_code/standard_core/jump_calc.sv b/source_code/standard_core/jump_calc.sv index ed957a1b5..7654aac18 100644 --- a/source_code/standard_core/jump_calc.sv +++ b/source_code/standard_core/jump_calc.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,22 +19,21 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 06/15/2016 -* Description: A simple adder for calculating branch targets +* Description: A simple adder for calculating branch targets */ `include "jump_calc_if.vh" -module jump_calc -( - jump_calc_if.jump_calc jump_if +module jump_calc ( + jump_calc_if.jump_calc jump_if ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; - word_t jump_addr; - assign jump_addr = jump_if.base + jump_if.offset; + word_t jump_addr; + assign jump_addr = jump_if.base + jump_if.offset; - assign jump_if.jal_addr = jump_addr; - assign jump_if.jalr_addr = {jump_addr[31:1], 1'b0}; + assign jump_if.jal_addr = jump_addr; + assign jump_if.jalr_addr = {jump_addr[31:1], 1'b0}; endmodule diff --git a/source_code/standard_core/memory_controller.sv b/source_code/standard_core/memory_controller.sv index 686fbd576..3e80c9740 100644 --- a/source_code/standard_core/memory_controller.sv +++ b/source_code/standard_core/memory_controller.sv @@ -1,21 +1,21 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: memory_controller.sv -* +* * Created by: John Skubic * Modified by: Chuan Yean Tan * Email: jskubic@purdue.edu , tan56@purdue.edu @@ -28,38 +28,36 @@ `include "component_selection_defines.vh" module memory_controller ( - input logic CLK, nRST, - generic_bus_if.generic_bus d_gen_bus_if, - generic_bus_if.generic_bus i_gen_bus_if, - generic_bus_if.cpu out_gen_bus_if + input logic CLK, + nRST, + generic_bus_if.generic_bus d_gen_bus_if, + generic_bus_if.generic_bus i_gen_bus_if, + generic_bus_if.cpu out_gen_bus_if ); - /* State Declaration */ - typedef enum { - IDLE, - INSTR_REQ , - INSTR_DATA_REQ, - INSTR_WAIT, - DATA_REQ , - DATA_INSTR_REQ , - DATA_WAIT - } state_t; - - state_t current_state, next_state; - - /* Internal Signals */ - logic [31:0] wdata, rdata; - - always_ff @ (posedge CLK, negedge nRST) - begin - if (nRST == 0) - current_state <= IDLE; - else - current_state <= next_state; - end - - /* State Transition Logic */ - /* + /* State Declaration */ + typedef enum { + IDLE, + INSTR_REQ, + INSTR_DATA_REQ, + INSTR_WAIT, + DATA_REQ, + DATA_INSTR_REQ, + DATA_WAIT + } state_t; + + state_t current_state, next_state; + + /* Internal Signals */ + logic [31:0] wdata, rdata; + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) current_state <= IDLE; + else current_state <= next_state; + end + + /* State Transition Logic */ + /* * Note: After interrupts were integrated, receiving an interrupt forces IREN * to go low. On an instruction request, the FSM assumed IREN high, and unconditionally * proceeded to an instruction wait state (either INSTR_WAIT or INSTR_DATA_REQ). However, @@ -73,161 +71,153 @@ module memory_controller ( * the instruction request in question should not be fetched since the next instruction should be from * the interrupt handler after the new PC is inserted. */ - always_comb - begin - case(current_state) - IDLE: begin - if(d_gen_bus_if.ren || d_gen_bus_if.wen) - next_state = DATA_REQ; - else if(i_gen_bus_if.ren) - next_state = INSTR_REQ; - else - next_state = IDLE; - end - - INSTR_REQ: begin - if(!i_gen_bus_if.ren) // Abort request, received an interrupt - next_state = IDLE; - else if(d_gen_bus_if.ren || d_gen_bus_if.wen) - next_state = INSTR_DATA_REQ; - else - next_state = INSTR_WAIT; - end - - INSTR_DATA_REQ : begin - if (out_gen_bus_if.busy == 1'b0) - next_state = DATA_WAIT; - else - next_state = INSTR_DATA_REQ; - end - - DATA_REQ: begin - if (i_gen_bus_if.ren) - next_state = DATA_INSTR_REQ; - else - next_state = DATA_WAIT; - end - - DATA_INSTR_REQ: begin - if(!i_gen_bus_if.ren && out_gen_bus_if.busy == 1'b0) // Abort request, received an interrupt - next_state = IDLE; - else if(out_gen_bus_if.busy == 1'b0) - next_state = INSTR_WAIT; - else - next_state = DATA_INSTR_REQ; - end - - INSTR_WAIT: begin - if ( out_gen_bus_if.busy == 1'b0 ) begin - if (d_gen_bus_if.ren || d_gen_bus_if.wen) - next_state = DATA_REQ; - else - next_state = IDLE; - end else if (d_gen_bus_if.ren || d_gen_bus_if.wen) - next_state = INSTR_DATA_REQ; - else - next_state = INSTR_WAIT; - end - - DATA_WAIT: begin - if ( out_gen_bus_if.busy == 1'b0 ) begin - if (i_gen_bus_if.ren) - next_state = INSTR_REQ; - else - next_state = IDLE; - end else if (i_gen_bus_if.ren) - next_state = DATA_INSTR_REQ; - else - next_state = DATA_WAIT; - end - - default: next_state = IDLE; - endcase - end - - /* State Output Logic */ - always_comb - begin - case(current_state) - IDLE: begin - out_gen_bus_if.wen = 0; - out_gen_bus_if.ren = 0; - out_gen_bus_if.addr = 0; - out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; - d_gen_bus_if.busy = 1'b1; - i_gen_bus_if.busy = 1'b1; - end - - //-- INSTRUCTION REQUEST --// - INSTR_REQ: begin - out_gen_bus_if.wen = i_gen_bus_if.wen; - out_gen_bus_if.ren = i_gen_bus_if.ren; - out_gen_bus_if.addr = i_gen_bus_if.addr; - out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; - d_gen_bus_if.busy = 1'b1; - i_gen_bus_if.busy = 1'b1; - end - INSTR_DATA_REQ: begin - out_gen_bus_if.wen = d_gen_bus_if.wen; - out_gen_bus_if.ren = d_gen_bus_if.ren; - out_gen_bus_if.addr = d_gen_bus_if.addr; - out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; - i_gen_bus_if.busy = out_gen_bus_if.busy; - d_gen_bus_if.busy = 1'b1; - end - INSTR_WAIT: begin - out_gen_bus_if.wen = 0; - out_gen_bus_if.ren = 0; - out_gen_bus_if.addr = 0; - out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; - d_gen_bus_if.busy = 1'b1; - i_gen_bus_if.busy = out_gen_bus_if.busy; - end - - //-- DATA REQUEST --// - DATA_REQ: begin - out_gen_bus_if.wen = d_gen_bus_if.wen; - out_gen_bus_if.ren = d_gen_bus_if.ren; - out_gen_bus_if.addr = d_gen_bus_if.addr; - out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; - d_gen_bus_if.busy = 1'b1; - i_gen_bus_if.busy = 1'b1; - end - DATA_INSTR_REQ: begin - out_gen_bus_if.wen = i_gen_bus_if.wen; - out_gen_bus_if.ren = i_gen_bus_if.ren; - out_gen_bus_if.addr = i_gen_bus_if.addr; - out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; - d_gen_bus_if.busy = out_gen_bus_if.busy; - i_gen_bus_if.busy = 1'b1; - end - DATA_WAIT: begin - out_gen_bus_if.wen = d_gen_bus_if.wen; - out_gen_bus_if.ren = d_gen_bus_if.ren; - out_gen_bus_if.addr = d_gen_bus_if.addr; - out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; - i_gen_bus_if.busy = 1'b1; - d_gen_bus_if.busy = out_gen_bus_if.busy; - end - endcase - end - - generate - if(BUS_ENDIANNESS == "big") - begin - assign wdata = d_gen_bus_if.wdata; - assign rdata = out_gen_bus_if.rdata; - end else if (BUS_ENDIANNESS == "little") - begin - logic [31:0] little_endian_wdata, little_endian_rdata; - endian_swapper wswap(d_gen_bus_if.wdata, little_endian_wdata); - endian_swapper rswap(out_gen_bus_if.rdata, little_endian_rdata); - assign wdata = little_endian_wdata; - assign rdata = little_endian_rdata; + always_comb begin + case (current_state) + IDLE: begin + if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = DATA_REQ; + else if (i_gen_bus_if.ren) next_state = INSTR_REQ; + else next_state = IDLE; + end + + INSTR_REQ: begin + if (!i_gen_bus_if.ren) // Abort request, received an interrupt + next_state = IDLE; + else if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = INSTR_DATA_REQ; + else next_state = INSTR_WAIT; + end + + INSTR_DATA_REQ: begin + if (out_gen_bus_if.busy == 1'b0) next_state = DATA_WAIT; + else next_state = INSTR_DATA_REQ; + end + + DATA_REQ: begin + if (i_gen_bus_if.ren) next_state = DATA_INSTR_REQ; + else next_state = DATA_WAIT; + end + + DATA_INSTR_REQ: begin + // Abort request, received an interrupt + if(!i_gen_bus_if.ren && out_gen_bus_if.busy == 1'b0) + next_state = IDLE; + else if (out_gen_bus_if.busy == 1'b0) next_state = INSTR_WAIT; + else next_state = DATA_INSTR_REQ; + end + + INSTR_WAIT: begin + if (out_gen_bus_if.busy == 1'b0) begin + if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = DATA_REQ; + else next_state = IDLE; + end else if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = INSTR_DATA_REQ; + else next_state = INSTR_WAIT; + end + + DATA_WAIT: begin + if (out_gen_bus_if.busy == 1'b0) begin + if (i_gen_bus_if.ren) next_state = INSTR_REQ; + else next_state = IDLE; + end else if (i_gen_bus_if.ren) next_state = DATA_INSTR_REQ; + else next_state = DATA_WAIT; + end + + default: next_state = IDLE; + endcase + end + + /* State Output Logic */ + always_comb begin + case (current_state) + IDLE: begin + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + i_gen_bus_if.busy = 1'b1; + end + + //-- INSTRUCTION REQUEST --// + INSTR_REQ: begin + out_gen_bus_if.wen = i_gen_bus_if.wen; + out_gen_bus_if.ren = i_gen_bus_if.ren; + out_gen_bus_if.addr = i_gen_bus_if.addr; + out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + i_gen_bus_if.busy = 1'b1; + end + INSTR_DATA_REQ: begin + out_gen_bus_if.wen = d_gen_bus_if.wen; + out_gen_bus_if.ren = d_gen_bus_if.ren; + out_gen_bus_if.addr = d_gen_bus_if.addr; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + i_gen_bus_if.busy = out_gen_bus_if.busy; + d_gen_bus_if.busy = 1'b1; + end + INSTR_WAIT: begin + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; + out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + i_gen_bus_if.busy = out_gen_bus_if.busy; + end + + //-- DATA REQUEST --// + DATA_REQ: begin + out_gen_bus_if.wen = d_gen_bus_if.wen; + out_gen_bus_if.ren = d_gen_bus_if.ren; + out_gen_bus_if.addr = d_gen_bus_if.addr; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + i_gen_bus_if.busy = 1'b1; + end + DATA_INSTR_REQ: begin + out_gen_bus_if.wen = i_gen_bus_if.wen; + out_gen_bus_if.ren = i_gen_bus_if.ren; + out_gen_bus_if.addr = i_gen_bus_if.addr; + out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; + d_gen_bus_if.busy = out_gen_bus_if.busy; + i_gen_bus_if.busy = 1'b1; + end + DATA_WAIT: begin + out_gen_bus_if.wen = d_gen_bus_if.wen; + out_gen_bus_if.ren = d_gen_bus_if.ren; + out_gen_bus_if.addr = d_gen_bus_if.addr; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + i_gen_bus_if.busy = 1'b1; + d_gen_bus_if.busy = out_gen_bus_if.busy; + end + default: begin + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + i_gen_bus_if.busy = 1'b1; + end + endcase end - endgenerate - assign out_gen_bus_if.wdata = wdata; - assign d_gen_bus_if.rdata = rdata; - assign i_gen_bus_if.rdata = rdata; + generate + if (BUS_ENDIANNESS == "big") begin : g_mc_bus_be + assign wdata = d_gen_bus_if.wdata; + assign rdata = out_gen_bus_if.rdata; + end else if (BUS_ENDIANNESS == "little") begin : g_mc_bus_le + logic [31:0] little_endian_wdata, little_endian_rdata; + endian_swapper wswap ( + .word_in(d_gen_bus_if.wdata), + .word_out(little_endian_wdata) + ); + endian_swapper rswap ( + .word_in(out_gen_bus_if.rdata), + .word_out(little_endian_rdata) + ); + assign wdata = little_endian_wdata; + assign rdata = little_endian_rdata; + end + endgenerate + + assign out_gen_bus_if.wdata = wdata; + assign d_gen_bus_if.rdata = rdata; + assign i_gen_bus_if.rdata = rdata; endmodule diff --git a/source_code/standard_core/rv32i_reg_file.sv b/source_code/standard_core/rv32i_reg_file.sv index 4328005b2..1fea48c83 100644 --- a/source_code/standard_core/rv32i_reg_file.sv +++ b/source_code/standard_core/rv32i_reg_file.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,25 +25,26 @@ `include "rv32i_reg_file_if.vh" module rv32i_reg_file ( - input CLK, nRST, - rv32i_reg_file_if.rf rf_if + input CLK, + nRST, + rv32i_reg_file_if.rf rf_if ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; - parameter NUM_REGS = 32; + localparam int NUM_REGS = 32; - word_t [NUM_REGS-1:0] registers; + word_t [NUM_REGS-1:0] registers; - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) begin - registers <= '0; - end else if (rf_if.wen && rf_if.rd) begin - registers[rf_if.rd] <= rf_if.w_data; + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + registers <= '0; + end else if (rf_if.wen && rf_if.rd) begin + registers[rf_if.rd] <= rf_if.w_data; + end end - end - assign rf_if.rs1_data = registers[rf_if.rs1]; - assign rf_if.rs2_data = registers[rf_if.rs2]; + assign rf_if.rs1_data = registers[rf_if.rs1]; + assign rf_if.rs2_data = registers[rf_if.rs2]; endmodule diff --git a/source_code/standard_core/standard_core.core b/source_code/standard_core/standard_core.core new file mode 100644 index 000000000..0eef09e76 --- /dev/null +++ b/source_code/standard_core/standard_core.core @@ -0,0 +1,33 @@ +CAPI=2: +name: socet:riscv:riscv_standard:0.1.0 +description: Standard core files for RISCVBusiness + +filesets: + rtl: + files: + - alu.sv + - branch_res.sv + - dmem_extender.sv + - endian_swapper.sv + - memory_controller.sv + - control_unit.sv + - jump_calc.sv + - rv32i_reg_file.sv + - RISCVBusiness.sv + - top_core.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + lint: + filesets: + - rtl + description: Linting + default_tool: veriblelint + toplevel: top_core + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search'] + diff --git a/source_code/standard_core/top_core.sv b/source_code/standard_core/top_core.sv index 0d89e2fe6..63b31f469 100644 --- a/source_code/standard_core/top_core.sv +++ b/source_code/standard_core/top_core.sv @@ -1,57 +1,64 @@ `include "core_interrupt_if.vh" -`include "ahb_if.vh" `include "generic_bus_if.vh" module top_core #( - parameter RESET_PC = 32'h80000000 -)( - input CLK, nRST, - output wfi, halt, - // core_interrupt_if - input ext_int, ext_int_clear, - input soft_int, soft_int_clear, - input timer_int, timer_int_clear, + parameter logic [31:0] RESET_PC = 32'h80000000 +) ( + input CLK, + nRST, + output wfi, + halt, // generic bus if case - `ifdef BUS_INTERFACE_GENERIC_BUS +`ifdef BUS_INTERFACE_GENERIC_BUS input busy, input [31:0] rdata, - output ren, wen, + output ren, + wen, output [3:0] byte_en, - output [31:0] addr, wdata + output [31:0] addr, + wdata, // ahb if case - `elsif BUS_INTERFACE_AHB +`elsif BUS_INTERFACE_AHB // TODO - `endif +`endif + // core_interrupt_if + input ext_int, + ext_int_clear, + input soft_int, + soft_int_clear, + input timer_int, + timer_int_clear ); function [31:0] get_x28; // verilator public - get_x28 = CORE.execute_stage_i.REG_FILE_SEL.rf.registers[28]; + get_x28 = CORE.execute_stage_i.g_rfile_select.rf.registers[28]; endfunction - + + /* bind tspp_execute_stage cpu_tracker cpu_track1 ( - .CLK(CLK), - .wb_stall(wb_stall), - .instr(fetch_ex_if.fetch_ex_reg.instr), - .pc(fetch_ex_if.fetch_ex_reg.pc), - .opcode(cu_if.opcode), - .funct3(funct3), - .funct12(funct12), - .rs1(rf_if.rs1), - .rs2(rf_if.rs2), - .rd(rf_if.rd), - .imm_S(cu_if.imm_S), - .imm_I(cu_if.imm_I), - .imm_U(cu_if.imm_U), - .imm_UJ(imm_UJ_ext), - .imm_SB(cu_if.imm_SB), - .instr_30(instr_30) - ); + .CLK(CLK), + .wb_stall(wb_stall), + .instr(fetch_ex_if.fetch_ex_reg.instr), + .pc(fetch_ex_if.fetch_ex_reg.pc), + .opcode(cu_if.opcode), + .funct3(funct3), + .funct12(funct12), + .rs1(rf_if.rs1), + .rs2(rf_if.rs2), + .rd(rf_if.rd), + .imm_S(cu_if.imm_S), + .imm_I(cu_if.imm_I), + .imm_U(cu_if.imm_U), + .imm_UJ(imm_UJ_ext), + .imm_SB(cu_if.imm_SB), + .instr_30(instr_30) + );*/ - core_interrupt_if interrupt_if(); + core_interrupt_if interrupt_if (); assign interrupt_if.ext_int = ext_int; assign interrupt_if.ext_int_clear = ext_int_clear; assign interrupt_if.soft_int = soft_int; @@ -59,25 +66,24 @@ module top_core #( assign interrupt_if.timer_int = timer_int; assign interrupt_if.timer_int_clear = timer_int_clear; - `ifdef BUS_INTERFACE_GENERIC_BUS - generic_bus_if gen_bus_if(); - assign gen_bus_if.busy = busy; - assign gen_bus_if.rdata = rdata; - assign ren = gen_bus_if.ren; - assign wen = gen_bus_if.wen; - assign byte_en = gen_bus_if.byte_en; - assign addr = gen_bus_if.addr; - assign wdata = gen_bus_if.wdata; - `elsif BUS_INTERFACE_AHB - ahb_if ahb_master(); - // TODO - `endif +`ifdef BUS_INTERFACE_GENERIC_BUS + generic_bus_if gen_bus_if (); + assign gen_bus_if.busy = busy; + assign gen_bus_if.rdata = rdata; + assign ren = gen_bus_if.ren; + assign wen = gen_bus_if.wen; + assign byte_en = gen_bus_if.byte_en; + assign addr = gen_bus_if.addr; + assign wdata = gen_bus_if.wdata; +`elsif BUS_INTERFACE_AHB + ahb_if ahb_master (); + // TODO + +`elsif BUS_INTERFACE_APB + apb_if apb_requester (CLK, nRST); +`endif - RISCVBusiness #( - .RESET_PC(RESET_PC) - ) CORE( - .* - ); + RISCVBusiness #(.RESET_PC(RESET_PC)) CORE (.*); endmodule diff --git a/source_code/tb/tb_RISCVBusiness_self_test.sv b/source_code/tb/tb_RISCVBusiness_self_test.sv index e10d6a5c8..596aa9258 100644 --- a/source_code/tb/tb_RISCVBusiness_self_test.sv +++ b/source_code/tb/tb_RISCVBusiness_self_test.sv @@ -1,27 +1,27 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* -* Filename: tb_RISCVBusiness_self_test.sv -* +* +* +* Filename: tb_RISCVBusiness_self_test.sv +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 * Description: Testbench for running RISCVBusiness until a halt condition. * The test bench monitors memory location 0x1000 and prints -* the char representation of what is written. +* the char representation of what is written. */ `timescale 1ns/100ps @@ -34,9 +34,9 @@ `define RVBSELF_CLK_TIMEOUT 5000000 module tb_RISCVBusiness_self_test (); - + parameter PERIOD = 20; - + logic CLK, nRST; logic ram_control; // 1 -> CORE, 0 -> TB logic halt; @@ -51,7 +51,7 @@ module tb_RISCVBusiness_self_test (); generic_bus_if rvb_gen_bus_if(); generic_bus_if tb_gen_bus_if(); core_interrupt_if interrupt_if(); - + assign interrupt_if.timer_int = '0; assign interrupt_if.timer_int_clear = '0; assign interrupt_if.ext_int = '0; @@ -105,7 +105,7 @@ module tb_RISCVBusiness_self_test (); .update_predictor(predict_if.update_predictor), .prediction(predict_if.prediction), .branch_result(predict_if.branch_result) - ); + ); //Ramif Mux always_comb begin @@ -145,12 +145,12 @@ module tb_RISCVBusiness_self_test (); nRST = 0; ram_control = 1; clk_count = 0; - + @(posedge CLK); @(posedge CLK); nRST = 1; - + while (DUT.halt == 0 && clk_count != `RVBSELF_CLK_TIMEOUT) begin @(posedge CLK); clk_count++; @@ -167,10 +167,10 @@ module tb_RISCVBusiness_self_test (); // Check Register 28 to see if test passed or failed if (clk_count == `RVBSELF_CLK_TIMEOUT) $display("ERROR: Test timed out"); - else if(DUT.execute_stage_i.REG_FILE_SEL.rf.registers[28] != 32'h1) + else if(DUT.execute_stage_i.g_rfile_select.rf.registers[28] != 32'h1) $display("ERROR: Test %0d did not pass", - (DUT.execute_stage_i.REG_FILE_SEL.rf.registers[28] - 1)/2); - else + (DUT.execute_stage_i.g_rfile_select.rf.registers[28] - 1)/2); + else $display("SUCCESS"); $finish; @@ -178,8 +178,8 @@ module tb_RISCVBusiness_self_test (); task dump_stats(); logic [63:0] instret, cycles; - instret = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.instretfull; - cycles = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.cyclefull; + instret = DUT.priv_wrapper_i.priv_block_i.csr.instret_full; + cycles = DUT.priv_wrapper_i.priv_block_i.csr.cycles_full; if (cycles != clk_count) $info("Cycles CSR (%0d) != clk_count (%0d)", cycles, clk_count); stats_ptr = $fopen(`STATS_FILE_NAME, "w"); $fwrite(stats_ptr, "Instructions retired: %2d\n", instret); @@ -208,7 +208,7 @@ module tb_RISCVBusiness_self_test (); $fwrite(fptr, ":%2h%4h00%8h%2h\n", 8'h4, addr[15:0]>>2, data, checksum); end // add the EOL entry to the file - $fwrite(fptr, ":00000001FF"); + $fwrite(fptr, ":00000001FF"); endtask @@ -229,11 +229,10 @@ module tb_RISCVBusiness_self_test (); checksum = hex_line[7:0] + hex_line[15:8] + hex_line[23:16] + hex_line[31:24] + hex_line[39:32] + hex_line[47:40] + hex_line[55:48] + hex_line[63:56]; - + //take two's complement checksum = (~checksum) + 1; return checksum; endfunction endmodule - diff --git a/source_code/trackers/branch_tracker.sv b/source_code/trackers/branch_tracker.sv index 0f06ae000..651fcad8f 100644 --- a/source_code/trackers/branch_tracker.sv +++ b/source_code/trackers/branch_tracker.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,76 +19,78 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 09/26/2016 -* Description: Track performance stats associated with branch prediction +* Description: Track performance stats associated with branch prediction */ -`define STATS_FILE_NAME "stats.txt" -module branch_tracker( - input logic CLK, nRST, - input logic update_predictor, - input logic prediction, - input logic branch_result +`define STATS_FILE_NAME "stats.txt" +module branch_tracker ( + input logic CLK, + nRST, + input logic update_predictor, + input logic prediction, + input logic branch_result ); - // Branch Performance Signals - logic [63:0] prediction_count, misprediction_count, correct_pred_count, - pred_taken_count, pred_not_taken_count, - taken_incorrect_count, not_taken_incorrect_count; - - always_ff @(posedge CLK, negedge nRST) begin : tracked_registers - if (!nRST) begin - prediction_count <= '0; - misprediction_count <= '0; - correct_pred_count <= '0; - pred_taken_count <= '0; - pred_not_taken_count <= '0; - taken_incorrect_count <= '0; - not_taken_incorrect_count <= '0; - end else if (update_predictor & !prediction & !branch_result) begin - // Predicted not taken and branch result is not taken - prediction_count <= prediction_count + 1; - correct_pred_count <= correct_pred_count + 1; - pred_not_taken_count <= pred_not_taken_count + 1; - end else if (update_predictor & !prediction & branch_result) begin - // Predicted not taken and branch result is taken - prediction_count <= prediction_count + 1; - misprediction_count <= misprediction_count + 1; - pred_not_taken_count <= pred_not_taken_count + 1; - not_taken_incorrect_count <= not_taken_incorrect_count + 1; - end else if (update_predictor & prediction & !branch_result) begin - // Predicted taken and branch result is not taken - prediction_count <= prediction_count + 1; - misprediction_count <= misprediction_count + 1; - pred_taken_count <= pred_taken_count + 1; - taken_incorrect_count <= taken_incorrect_count + 1; - end else if (update_predictor & prediction & branch_result) begin - // Predicted taken and branch result is taken - prediction_count <= prediction_count + 1; - correct_pred_count <= correct_pred_count + 1; - pred_taken_count <= pred_taken_count + 1; - end - end : tracked_registers + // Branch Performance Signals + logic [63:0] + prediction_count, + misprediction_count, + correct_pred_count, + pred_taken_count, + pred_not_taken_count, + taken_incorrect_count, + not_taken_incorrect_count; + + always_ff @(posedge CLK, negedge nRST) begin : tracked_registers + if (!nRST) begin + prediction_count <= '0; + misprediction_count <= '0; + correct_pred_count <= '0; + pred_taken_count <= '0; + pred_not_taken_count <= '0; + taken_incorrect_count <= '0; + not_taken_incorrect_count <= '0; + end else if (update_predictor & !prediction & !branch_result) begin + // Predicted not taken and branch result is not taken + prediction_count <= prediction_count + 1; + correct_pred_count <= correct_pred_count + 1; + pred_not_taken_count <= pred_not_taken_count + 1; + end else if (update_predictor & !prediction & branch_result) begin + // Predicted not taken and branch result is taken + prediction_count <= prediction_count + 1; + misprediction_count <= misprediction_count + 1; + pred_not_taken_count <= pred_not_taken_count + 1; + not_taken_incorrect_count <= not_taken_incorrect_count + 1; + end else if (update_predictor & prediction & !branch_result) begin + // Predicted taken and branch result is not taken + prediction_count <= prediction_count + 1; + misprediction_count <= misprediction_count + 1; + pred_taken_count <= pred_taken_count + 1; + taken_incorrect_count <= taken_incorrect_count + 1; + end else if (update_predictor & prediction & branch_result) begin + // Predicted taken and branch result is taken + prediction_count <= prediction_count + 1; + correct_pred_count <= correct_pred_count + 1; + pred_taken_count <= pred_taken_count + 1; + end + end : tracked_registers - final begin : OUTPUT_STATS - integer stats_fptr; - stats_fptr = $fopen(`STATS_FILE_NAME, "a"); - $fwrite(stats_fptr, "Conditional branches predicted: %2d\n", prediction_count); - $fwrite(stats_fptr, "Conditional branches predicted incorrectly: %2d\n", - misprediction_count); - $fwrite(stats_fptr, "Conditional branches predicted correctly: %2d\n", - correct_pred_count); - $fwrite(stats_fptr, "Branch prediction accuracy: %5f\n", - real'(correct_pred_count)/prediction_count); - $fwrite(stats_fptr, "Branches predicted as taken: %2d\n", - pred_taken_count); - $fwrite(stats_fptr, "Branches predicted as taken, incorrect: %2d\n", - taken_incorrect_count); - $fwrite(stats_fptr, "Branches predicted as taken, correct: %2d\n", - pred_taken_count - taken_incorrect_count); - $fwrite(stats_fptr, "Branches predicted as not taken: %2d\n", - pred_not_taken_count); - $fwrite(stats_fptr, "Branches predicted as not taken, incorrect: %2d\n", - not_taken_incorrect_count); - $fwrite(stats_fptr, "Branches predicted as not taken, correct: %2d\n", - pred_not_taken_count - not_taken_incorrect_count); - $fclose(stats_fptr); - end : OUTPUT_STATS + final begin : OUTPUT_STATS + integer stats_fptr; + stats_fptr = $fopen(`STATS_FILE_NAME, "a"); + $fwrite(stats_fptr, "Conditional branches predicted: %2d\n", prediction_count); + $fwrite(stats_fptr, "Conditional branches predicted incorrectly: %2d\n", + misprediction_count); + $fwrite(stats_fptr, "Conditional branches predicted correctly: %2d\n", correct_pred_count); + $fwrite(stats_fptr, "Branch prediction accuracy: %5f\n", + real'(correct_pred_count) / prediction_count); + $fwrite(stats_fptr, "Branches predicted as taken: %2d\n", pred_taken_count); + $fwrite(stats_fptr, "Branches predicted as taken, incorrect: %2d\n", taken_incorrect_count); + $fwrite(stats_fptr, "Branches predicted as taken, correct: %2d\n", + pred_taken_count - taken_incorrect_count); + $fwrite(stats_fptr, "Branches predicted as not taken: %2d\n", pred_not_taken_count); + $fwrite(stats_fptr, "Branches predicted as not taken, incorrect: %2d\n", + not_taken_incorrect_count); + $fwrite(stats_fptr, "Branches predicted as not taken, correct: %2d\n", + pred_not_taken_count - not_taken_incorrect_count); + $fclose(stats_fptr); + end : OUTPUT_STATS endmodule diff --git a/source_code/trackers/cpu_tracker.core b/source_code/trackers/cpu_tracker.core new file mode 100644 index 000000000..d2fec27e6 --- /dev/null +++ b/source_code/trackers/cpu_tracker.core @@ -0,0 +1,21 @@ +CAPI=2: +name: socet:riscv:cpu-tracker:0.1.0 +description: RISC-V Tracker modules + +filesets: + rtl: + files: + - cpu_tracker.sv + file_type: systemVerilogSource + +targets: + lint: + filesets: + - rtl + description: Linting + default_tool: veriblelint + toplevel: cpu_tracker + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search'] + diff --git a/source_code/trackers/cpu_tracker.sv b/source_code/trackers/cpu_tracker.sv index d35d27b95..1115fca12 100644 --- a/source_code/trackers/cpu_tracker.sv +++ b/source_code/trackers/cpu_tracker.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,260 +20,238 @@ * Email: steven69@purdue.edu * Date Created: 06/27/2016 * Description: Prints out a trace of the cpu executing that can be -* compared against the trace generated by Spike +* compared against the trace generated by Spike */ `define TRACE_FILE_NAME "trace.log" -import rv32i_types_pkg::*; -import machine_mode_types_pkg::*; -module cpu_tracker( - input logic CLK, wb_stall, instr_30, - input word_t instr, pc, - input opcode_t opcode, - input logic [2:0] funct3, - input logic [11:0] funct12, - input logic [4:0] rs1, rs2, rd, - input logic [12:0] imm_SB, - input logic [11:0] imm_S, imm_I, - input logic [20:0] imm_UJ, - input logic [31:0] imm_U +module cpu_tracker ( + input logic CLK, wb_stall, instr_30, + input rv32i_types_pkg::word_t instr, pc, + input rv32i_types_pkg::opcode_t opcode, + input logic [2:0] funct3, + input logic [11:0] funct12, + input logic [4:0] rs1, rs2, rd, + input logic [12:0] imm_SB, + input logic [11:0] imm_S, imm_I, + input logic [20:0] imm_UJ, + input logic [31:0] imm_U ); - parameter CPUID = 0; + import rv32i_types_pkg::*; + import machine_mode_types_1_12_pkg::*; - integer fptr; - string instr_mnemonic, output_str, src1, src2, dest, operands; - string csr, temp_str; - logic [63:0] pc64; - assign pc64 = {{32{1'b0}}, pc}; - initial begin: INIT_FILE - fptr = $fopen(`TRACE_FILE_NAME, "w"); - end + parameter int CPUID = 0; - always_comb begin - src1 = registerAssign(rs1); - src2 = registerAssign(rs2); - dest = registerAssign(rd); - csr = csrRegisterAssign(funct12); - end + integer fptr; + string instr_mnemonic, output_str, src1, src2, dest, operands; + string csr, temp_str; + logic [63:0] pc64; + assign pc64 = {{32{1'b0}}, pc}; + initial begin : INIT_FILE + fptr = $fopen(`TRACE_FILE_NAME, "w"); + end - always_comb begin - case (opcode) - LUI, AUIPC: $sformat(operands, "%s, %d", dest, imm_U[31:12]); - JAL: $sformat(operands, "%s, pc + %d", dest, signed'(imm_UJ)); - JALR: $sformat(operands, "%s, %s, %d", dest, src1, signed'(imm_I)); - BRANCH: $sformat(operands, "%s, %s, pc + %d", src1, src2, signed'(imm_SB)); - STORE: $sformat(operands, "%s, %d(%s)", src2, signed'(imm_S), src1); - LOAD: $sformat(operands, "%s, %d(%s)", dest, signed'(imm_I), src1); - IMMED: $sformat(operands, "%s, %s, %d", dest, src1, signed'(imm_I)); - REGREG: $sformat(operands, "%s, %s, %s", dest, src1, src2); - SYSTEM: begin - case(rv32i_system_t'(funct3)) - CSRRS, CSRRW, - CSRRC: $sformat(operands, "%s, %s, %s", dest, csr, src1); - CSRRSI, CSRRWI, - CSRRCI: $sformat(operands, "%s, %s, %d", dest, csr, rs1); - PRIV: operands = ""; - endcase - end - default: operands = ""; - endcase - end + always_comb begin + src1 = registerAssign(rs1); + src2 = registerAssign(rs2); + dest = registerAssign(rd); + csr = csrRegisterAssign(funct12); + end - always_comb begin - case (opcode) - LUI: instr_mnemonic = "lui"; - AUIPC: instr_mnemonic = "auipc"; - JAL: instr_mnemonic = "jal"; - JALR: instr_mnemonic = "jalr"; - BRANCH: begin - case(branch_t'(funct3)) - BEQ: instr_mnemonic = "beq"; - BNE: instr_mnemonic = "bne"; - BLT: instr_mnemonic = "blt"; - BGE: instr_mnemonic = "bge"; - BLTU: instr_mnemonic = "bltu"; - BGEU: instr_mnemonic = "bgeu"; - default: instr_mnemonic = "unknown branch op"; - endcase - end - LOAD: begin - case(load_t'(funct3)) - LB: instr_mnemonic = "lb"; - LH: instr_mnemonic = "lh"; - LW: instr_mnemonic = "lw"; - LBU: instr_mnemonic = "lbu"; - LHU: instr_mnemonic = "lhu"; - default: instr_mnemonic = "unknown load op"; - endcase - end - STORE: begin - case(store_t'(funct3)) - SB: instr_mnemonic = "sb"; - SH: instr_mnemonic = "sh"; - SW: instr_mnemonic = "sw"; - default: instr_mnemonic = "unknown store op"; - endcase - end - IMMED: begin - case(imm_t'(funct3)) - ADDI: instr_mnemonic = "addi"; - SLTI: instr_mnemonic = "slti"; - SLTIU: instr_mnemonic = "sltiu"; - XORI: instr_mnemonic = "xori"; - ORI: instr_mnemonic = "ori"; - ANDI: instr_mnemonic = "andi"; - SLLI: instr_mnemonic = "slli"; - SRI: begin - if (instr_30) - instr_mnemonic = "srai"; - else - instr_mnemonic = "srli"; - end - default: instr_mnemonic = "unknown immed op"; + always_comb begin + case (opcode) + LUI, AUIPC: $sformat(operands, "%s, %d", dest, imm_U[31:12]); + JAL: $sformat(operands, "%s, pc + %d", dest, signed'(imm_UJ)); + JALR: $sformat(operands, "%s, %s, %d", dest, src1, signed'(imm_I)); + BRANCH: $sformat(operands, "%s, %s, pc + %d", src1, src2, signed'(imm_SB)); + STORE: $sformat(operands, "%s, %d(%s)", src2, signed'(imm_S), src1); + LOAD: $sformat(operands, "%s, %d(%s)", dest, signed'(imm_I), src1); + IMMED: $sformat(operands, "%s, %s, %d", dest, src1, signed'(imm_I)); + REGREG: $sformat(operands, "%s, %s, %s", dest, src1, src2); + SYSTEM: begin + case (rv32i_system_t'(funct3)) + CSRRS, CSRRW, CSRRC: $sformat(operands, "%s, %s, %s", dest, csr, src1); + CSRRSI, CSRRWI, CSRRCI: $sformat(operands, "%s, %s, %d", dest, csr, rs1); + PRIV: operands = ""; + default: operands = ""; + endcase + end + default: operands = ""; endcase - end - REGREG: begin - case(regreg_t'(funct3)) - ADDSUB: begin - if (instr_30) - instr_mnemonic = "sub"; - else - instr_mnemonic = "add"; - end - SLL: instr_mnemonic = "sll"; - SLT: instr_mnemonic = "slt"; - SLTU: instr_mnemonic = "sltu"; - XOR: instr_mnemonic = "xor"; - SR: begin - if (instr_30) - instr_mnemonic = "sra"; - else - instr_mnemonic = "srl"; - end - OR: instr_mnemonic = "or"; - AND: instr_mnemonic = "and"; - default: instr_mnemonic = "unknown regreg op"; - endcase - end - SYSTEM: begin - case(rv32i_system_t'(funct3)) - CSRRW: instr_mnemonic = "csrrw"; - CSRRS: instr_mnemonic = "csrrs"; - CSRRC: instr_mnemonic = "csrrc"; - CSRRWI: instr_mnemonic = "csrrwi"; - CSRRSI: instr_mnemonic = "csrrsi"; - CSRRCI: instr_mnemonic = "csrrci"; - PRIV: begin - case(priv_insn_t'(funct12)) - ECALL: instr_mnemonic = "ecall"; - EBREAK: instr_mnemonic = "ebreak"; - MRET: instr_mnemonic = "mret"; - default: begin - instr_mnemonic = "errr"; - $display("%b", priv_insn_t'(funct12)); - end - endcase - end - default: instr_mnemonic = "unknown system op"; - endcase - end - MISCMEM: begin - case(rv32i_miscmem_t'(funct3)) - FENCE: instr_mnemonic = "fence"; - FENCEI: instr_mnemonic = "fence.i"; - default: instr_mnemonic = "unknown misc-mem op"; + end + + always_comb begin + case (opcode) + LUI: instr_mnemonic = "lui"; + AUIPC: instr_mnemonic = "auipc"; + JAL: instr_mnemonic = "jal"; + JALR: instr_mnemonic = "jalr"; + BRANCH: begin + case (branch_t'(funct3)) + BEQ: instr_mnemonic = "beq"; + BNE: instr_mnemonic = "bne"; + BLT: instr_mnemonic = "blt"; + BGE: instr_mnemonic = "bge"; + BLTU: instr_mnemonic = "bltu"; + BGEU: instr_mnemonic = "bgeu"; + default: instr_mnemonic = "unknown branch op"; + endcase + end + LOAD: begin + case (load_t'(funct3)) + LB: instr_mnemonic = "lb"; + LH: instr_mnemonic = "lh"; + LW: instr_mnemonic = "lw"; + LBU: instr_mnemonic = "lbu"; + LHU: instr_mnemonic = "lhu"; + default: instr_mnemonic = "unknown load op"; + endcase + end + STORE: begin + case (store_t'(funct3)) + SB: instr_mnemonic = "sb"; + SH: instr_mnemonic = "sh"; + SW: instr_mnemonic = "sw"; + default: instr_mnemonic = "unknown store op"; + endcase + end + IMMED: begin + case (imm_t'(funct3)) + ADDI: instr_mnemonic = "addi"; + SLTI: instr_mnemonic = "slti"; + SLTIU: instr_mnemonic = "sltiu"; + XORI: instr_mnemonic = "xori"; + ORI: instr_mnemonic = "ori"; + ANDI: instr_mnemonic = "andi"; + SLLI: instr_mnemonic = "slli"; + SRI: begin + if (instr_30) instr_mnemonic = "srai"; + else instr_mnemonic = "srli"; + end + default: instr_mnemonic = "unknown immed op"; + endcase + end + REGREG: begin + case (regreg_t'(funct3)) + ADDSUB: begin + if (instr_30) instr_mnemonic = "sub"; + else instr_mnemonic = "add"; + end + SLL: instr_mnemonic = "sll"; + SLT: instr_mnemonic = "slt"; + SLTU: instr_mnemonic = "sltu"; + XOR: instr_mnemonic = "xor"; + SR: begin + if (instr_30) instr_mnemonic = "sra"; + else instr_mnemonic = "srl"; + end + OR: instr_mnemonic = "or"; + AND: instr_mnemonic = "and"; + default: instr_mnemonic = "unknown regreg op"; + endcase + end + SYSTEM: begin + case (rv32i_system_t'(funct3)) + CSRRW: instr_mnemonic = "csrrw"; + CSRRS: instr_mnemonic = "csrrs"; + CSRRC: instr_mnemonic = "csrrc"; + CSRRWI: instr_mnemonic = "csrrwi"; + CSRRSI: instr_mnemonic = "csrrsi"; + CSRRCI: instr_mnemonic = "csrrci"; + PRIV: begin + case (priv_insn_t'(funct12)) + ECALL: instr_mnemonic = "ecall"; + EBREAK: instr_mnemonic = "ebreak"; + MRET: instr_mnemonic = "mret"; + default: begin + instr_mnemonic = "errr"; + $display("%b", priv_insn_t'(funct12)); + end + endcase + end + default: instr_mnemonic = "unknown system op"; + endcase + end + MISCMEM: begin + case (rv32i_miscmem_t'(funct3)) + FENCE: instr_mnemonic = "fence"; + FENCEI: instr_mnemonic = "fence.i"; + default: instr_mnemonic = "unknown misc-mem op"; + endcase + end + default: instr_mnemonic = "xxx"; endcase - end - default: instr_mnemonic = "xxx"; - endcase - end + end - function string registerAssign(input logic [4:0] register); - case (register) - 5'd0: registerAssign = "zero"; - 5'd1: registerAssign = "ra"; - 5'd2: registerAssign = "sp"; - 5'd3: registerAssign = "gp"; - 5'd4: registerAssign = "tp"; - 5'd5: registerAssign = "t0"; - 5'd6: registerAssign = "t1"; - 5'd7: registerAssign = "t2"; - 5'd8: registerAssign = "s0"; - 5'd9: registerAssign = "s1"; - 5'd10: registerAssign = "a0"; - 5'd11: registerAssign = "a1"; - 5'd12: registerAssign = "a2"; - 5'd13: registerAssign = "a3"; - 5'd14: registerAssign = "a4"; - 5'd15: registerAssign = "a5"; - 5'd16: registerAssign = "a6"; - 5'd17: registerAssign = "a7"; - 5'd18: registerAssign = "s2"; - 5'd19: registerAssign = "s3"; - 5'd20: registerAssign = "s4"; - 5'd21: registerAssign = "s5"; - 5'd22: registerAssign = "s6"; - 5'd23: registerAssign = "s7"; - 5'd24: registerAssign = "s8"; - 5'd25: registerAssign = "s9"; - 5'd26: registerAssign = "s10"; - 5'd27: registerAssign = "s11"; - 5'd28: registerAssign = "t3"; - 5'd29: registerAssign = "t4"; - 5'd30: registerAssign = "t5"; - 5'd31: registerAssign = "t6"; - endcase - endfunction + function string registerAssign(input logic [4:0] register); + case (register) + 5'd0: registerAssign = "zero"; + 5'd1: registerAssign = "ra"; + 5'd2: registerAssign = "sp"; + 5'd3: registerAssign = "gp"; + 5'd4: registerAssign = "tp"; + 5'd5: registerAssign = "t0"; + 5'd6: registerAssign = "t1"; + 5'd7: registerAssign = "t2"; + 5'd8: registerAssign = "s0"; + 5'd9: registerAssign = "s1"; + 5'd10: registerAssign = "a0"; + 5'd11: registerAssign = "a1"; + 5'd12: registerAssign = "a2"; + 5'd13: registerAssign = "a3"; + 5'd14: registerAssign = "a4"; + 5'd15: registerAssign = "a5"; + 5'd16: registerAssign = "a6"; + 5'd17: registerAssign = "a7"; + 5'd18: registerAssign = "s2"; + 5'd19: registerAssign = "s3"; + 5'd20: registerAssign = "s4"; + 5'd21: registerAssign = "s5"; + 5'd22: registerAssign = "s6"; + 5'd23: registerAssign = "s7"; + 5'd24: registerAssign = "s8"; + 5'd25: registerAssign = "s9"; + 5'd26: registerAssign = "s10"; + 5'd27: registerAssign = "s11"; + 5'd28: registerAssign = "t3"; + 5'd29: registerAssign = "t4"; + 5'd30: registerAssign = "t5"; + 5'd31: registerAssign = "t6"; + default: registerAssign = "UNKNOWN REGISTER"; + endcase + endfunction - function string csrRegisterAssign(input logic [11:0] csr_register); - case (csr_addr_t'(csr_register)) - MVENDORID_ADDR : csrRegisterAssign = "mvendorid"; - MARCHID_ADDR : csrRegisterAssign = "marchid"; - MIMPID_ADDR : csrRegisterAssign = "mimpid"; - MHARTID_ADDR : csrRegisterAssign = "mhartid"; - MSTATUS_ADDR : csrRegisterAssign = "mstatus"; - MISA_ADDR : csrRegisterAssign = "misa"; - MEDELEG_ADDR : csrRegisterAssign = "medeleg"; - MIDELEG_ADDR : csrRegisterAssign = "mideleg"; - MTVEC_ADDR : csrRegisterAssign = "mtvec"; - MIE_ADDR : csrRegisterAssign = "mie"; - MSCRATCH_ADDR : csrRegisterAssign = "mscratch"; - MEPC_ADDR : csrRegisterAssign = "mepc"; - MCAUSE_ADDR : csrRegisterAssign = "mcause"; - MTVAL_ADDR : csrRegisterAssign = "mtval"; - MIP_ADDR : csrRegisterAssign = "mip"; - // TODO: MAY BE ABLE TO REMOVE BELOW - MBASE_ADDR : csrRegisterAssign = "mbase"; - MBOUND_ADDR : csrRegisterAssign = "mbound"; - MIBASE_ADDR : csrRegisterAssign = "mibase"; - MIBOUND_ADDR : csrRegisterAssign = "mibound"; - MDBASE_ADDR : csrRegisterAssign = "mdbase"; - MDBOUND_ADDR : csrRegisterAssign = "mdbound"; - // TODO: MAY BE ABLE TO REMOVE ABOVE - // TODO: BELOW MUST BE REMOVED - HTIMEW_ADDR : csrRegisterAssign = "htimew"; - HTIMEHW_ADDR : csrRegisterAssign = "htimehw"; - MTIMECMP_ADDR : csrRegisterAssign = "mtimecmp"; - MTIME_ADDR : csrRegisterAssign = "mtime"; - MTIMEH_ADDR : csrRegisterAssign = "mtimeh"; - MTOHOST_ADDR : csrRegisterAssign = "mtohost"; - MFROMHOST_ADDR : csrRegisterAssign = "mfromhost"; - // TODO: ABOVE MUST BE REMOVED - default : csrRegisterAssign = "csr register not tracked"; - endcase - endfunction + function string csrRegisterAssign(input logic [11:0] csr_register); + case (csr_addr_t'(csr_register)) + MVENDORID_ADDR: csrRegisterAssign = "mvendorid"; + MARCHID_ADDR: csrRegisterAssign = "marchid"; + MIMPID_ADDR: csrRegisterAssign = "mimpid"; + MHARTID_ADDR: csrRegisterAssign = "mhartid"; + MSTATUS_ADDR: csrRegisterAssign = "mstatus"; + MISA_ADDR: csrRegisterAssign = "misa"; + MEDELEG_ADDR: csrRegisterAssign = "medeleg"; + MIDELEG_ADDR: csrRegisterAssign = "mideleg"; + MTVEC_ADDR: csrRegisterAssign = "mtvec"; + MIE_ADDR: csrRegisterAssign = "mie"; + MSCRATCH_ADDR: csrRegisterAssign = "mscratch"; + MEPC_ADDR: csrRegisterAssign = "mepc"; + MCAUSE_ADDR: csrRegisterAssign = "mcause"; + MTVAL_ADDR: csrRegisterAssign = "mtval"; + MIP_ADDR: csrRegisterAssign = "mip"; + default: csrRegisterAssign = "csr register not tracked"; + endcase + endfunction - always_ff @ (posedge CLK) begin - if (!wb_stall && instr != 0) begin - $sformat(temp_str, "core%d: 0x%h (0x%h)", CPUID, pc64, instr); - $sformat(output_str, "%s %s %s\n", temp_str, instr_mnemonic, operands); - $fwrite(fptr, output_str); + always_ff @(posedge CLK) begin + if (!wb_stall && instr != 0) begin + $sformat(temp_str, "core%d: 0x%h (0x%h)", CPUID, pc64, instr); + $sformat(output_str, "%s %s %s\n", temp_str, instr_mnemonic, operands); + $fwrite(fptr, output_str); + end end - end - final begin: CLOSE_FILE - $fclose(fptr); - end + final begin : CLOSE_FILE + $fclose(fptr); + end endmodule diff --git a/source_code/wscript b/source_code/wscript deleted file mode 100644 index 4b1845f2a..000000000 --- a/source_code/wscript +++ /dev/null @@ -1,143 +0,0 @@ -def configure(ctx): - rvb_srcs = ['caches', 'branch_predictors', 'bus_bridges', 'trackers', 'risc_mgmt', 'privs', 'pipelines', 'standard_core', 'ram', 'sparce', 'rv32c', 'RV32E'] - ctx.SFFUnits.add('RISCVBusiness_packages', - src_dir = ['packages'] - ) - ctx.SFFUnits.add('RISCVBusiness', - includes = ['include'], - src_dir = rvb_srcs, - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_RISCVBusiness', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('RISCVBusiness_self_test', - includes = ['include'], - src_dir = rvb_srcs, - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_RISCVBusiness_self_test', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('RISCVBusiness_self_test_plic', - includes = ['include'], - src_dir = rvb_srcs, - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_RISCVBusiness_self_test_plic', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('alu', - includes = ['include'], - src_dir = ['standard_core'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_alu', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('ahb_master', - includes = ['include'], - src_dir = ['bus_bridges', 'standard_core', 'ram'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_ahb_master', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('memory_controller', - includes = ['include'], - src_dir = ['bus_bridges', 'standard_core', 'ram'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_memory_controller', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('risc_mgmt', - includes = ['include'], - src_dir = ['risc_mgmt'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_risc_mgmt', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('shift_add_multiplier', - includes = ['include'], - src_dir = ['risc_mgmt'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_shift_add_multiplier', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('shift_test_restore_divider', - includes = ['include'], - src_dir = ['risc_mgmt'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_shift_test_restore_divider', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('caches', - includes = ['include'], - src_dir = ['ram', 'bus_bridges', 'standard_core', 'caches'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_caches', - tb_includes = ['include'] - ) - - ctx.SFFUnits.add('priv_1_11_block', - includes = ['include'], - src_dir = ['privs/priv_1_11'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_priv_1_11_block', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('priv_1_11_control', - includes = ['include'], - src_dir = ['privs/priv_1_11'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_priv_1_11_control', - tb_includes = ['include'] - ) - - ctx.SFFUnits.add('sparce_svc', - includes = ['include'], - src_dir = ['sparce'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_sparce_svc', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('sparce_sprf', - includes = ['include'], - src_dir = ['sparce'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_sparce_sprf', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('sparce_sasa_table', - includes = ['include'], - src_dir = ['sparce'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_sparce_sasa_table', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('sparce_psru', - includes = ['include'], - src_dir = ['sparce'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_sparce_psru', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('sparce_cfid', - includes = ['include'], - src_dir = ['sparce'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_sparce_cfid', - tb_includes = ['include'] - ) diff --git a/wscript b/wscript deleted file mode 100644 index d8fee75a2..000000000 --- a/wscript +++ /dev/null @@ -1,31 +0,0 @@ -#! /usr/bin/env python -# encoding: utf-8 - -import os -toolpath = os.environ['WAFDIR'] + '/../waf-extensions' - -top = '.' -out = 'build' - -def options(ctx): - if len(ctx.stack_path) == 1 and ctx.stack_path[0] is None: - ctx.load('SFFbuildmgr', tooldir=toolpath) - ctx.load('SFFbuild', tooldir=toolpath) - #ctx.load('Syn_support', tooldir=toolpath) - ctx.load('why') - else: - pass - -def configure(ctx): - if len(ctx.stack_path) == 1 and ctx.stack_path[0] is None: - ctx.load('SFFbuildmgr', tooldir=toolpath) - ctx.load('SFFbuild', tooldir=toolpath) - ctx.recurse('source_code') - if len(ctx.stack_path) == 1 and ctx.stack_path[0] is None: - ctx.SFFUnits.finalize() - -def sim_source(ctx): - ctx.recurse('source_code') - -def build(ctx): - pass From 9f09e77a40d523bdc5f5bff5a5a95a91d94dbb8e Mon Sep 17 00:00:00 2001 From: Hadi Ahmed Date: Mon, 31 Oct 2022 12:44:47 -0400 Subject: [PATCH 16/47] Priv 1.12 PMA integration (#21) RISC-V PMA implementation for Privileged Spec 1.12. --- Makefile | 4 +- source_code/include/priv_1_12_internal_if.vh | 12 ++ source_code/include/prv_pipeline_if.vh | 21 ++- source_code/packages/pma_types_1_12_pkg.sv | 78 ++++++++ source_code/packages/riscv_packages.core | 1 + .../pipelines/tspp/tspp_execute_stage.sv | 12 +- .../pipelines/tspp/tspp_fetch_stage.sv | 10 +- .../pipelines/tspp/tspp_hazard_unit.sv | 10 +- source_code/privs/priv.core | 1 + .../privs/priv_1_12/priv_1_12_block.sv | 17 +- source_code/privs/priv_1_12/priv_1_12_csr.sv | 23 ++- source_code/privs/priv_1_12/priv_1_12_pma.sv | 126 +++++++++++++ source_code/standard_core/RISCVBusiness.sv | 3 +- source_code/tb/tb_priv_1_12_block.sv | 8 +- tb_core.cc | 16 +- verification/interrupt-tests/ext_interrupt.S | 171 ------------------ verification/interrupt-tests/interrupts.S | 52 ------ verification/interrupt-tests/soft_interrupt.S | 79 -------- verification/interrupts-exceptions/utility.h | 16 +- verification/pma-tests/build_all.py | 51 ++++++ verification/pma-tests/link.ld | 14 ++ verification/pma-tests/pma.c | 37 ++++ verification/pma-tests/start.S | 17 ++ verification/pma-tests/utility.c | 25 +++ verification/pma-tests/utility.h | 19 ++ 25 files changed, 477 insertions(+), 346 deletions(-) create mode 100644 source_code/packages/pma_types_1_12_pkg.sv create mode 100644 source_code/privs/priv_1_12/priv_1_12_pma.sv delete mode 100644 verification/interrupt-tests/ext_interrupt.S delete mode 100644 verification/interrupt-tests/interrupts.S delete mode 100644 verification/interrupt-tests/soft_interrupt.S create mode 100755 verification/pma-tests/build_all.py create mode 100755 verification/pma-tests/link.ld create mode 100644 verification/pma-tests/pma.c create mode 100644 verification/pma-tests/start.S create mode 100644 verification/pma-tests/utility.c create mode 100644 verification/pma-tests/utility.h diff --git a/Makefile b/Makefile index 421a6482d..c89ea5808 100644 --- a/Makefile +++ b/Makefile @@ -15,11 +15,11 @@ RV32M_FILES := $(RISC_MGMT)/extensions/rv32m/carry_save_adder.sv $(RISC_MGMT)/ex RV32C_FILES := $(RV32C)/decompressor.sv $(RV32C)/fetch_buffer.sv $(RV32C)/rv32c_disabled.sv $(RV32C)/rv32c_enabled.sv $(RV32C)/rv32c_wrapper.sv RISC_MGMT_FILES := $(RISC_MGMT)/risc_mgmt_wrapper.sv $(RISC_MGMT)/tspp/tspp_risc_mgmt.sv $(RV32M_FILES) RISC_EXT_FILES := $(RISC_MGMT)/extensions/template/template_decode.sv $(RISC_MGMT)/extensions/template/template_execute.sv $(RISC_MGMT)/extensions/template/template_memory.sv -CORE_PKG_FILES := $(RISCV_PKGS)/rv32i_types_pkg.sv $(RISCV_PKGS)/alu_types_pkg.sv $(RISCV_PKGS)/risc_mgmt/template_pkg.sv $(RISCV_PKGS)/risc_mgmt/crc32_pkg.sv $(RISCV_PKGS)/risc_mgmt/rv32m_pkg.sv $(RISCV_PKGS)/risc_mgmt/test_pkg.sv $(RISCV_PKGS)/machine_mode_types_1_12_pkg.sv $(RISCV_PKGS)/machine_mode_types_pkg.sv +CORE_PKG_FILES := $(RISCV_PKGS)/rv32i_types_pkg.sv $(RISCV_PKGS)/alu_types_pkg.sv $(RISCV_PKGS)/risc_mgmt/template_pkg.sv $(RISCV_PKGS)/risc_mgmt/crc32_pkg.sv $(RISCV_PKGS)/risc_mgmt/rv32m_pkg.sv $(RISCV_PKGS)/risc_mgmt/test_pkg.sv $(RISCV_PKGS)/machine_mode_types_pkg.sv $(RISCV_PKGS)/machine_mode_types_1_12_pkg.sv $(RISCV_PKGS)/pma_types_1_12_pkg.sv CORE_FILES := $(RISCV_CORE)/alu.sv $(RISCV_CORE)/branch_res.sv $(RISCV_CORE)/control_unit.sv $(RISCV_CORE)/dmem_extender.sv $(RISCV_CORE)/endian_swapper.sv $(RISCV_CORE)/jump_calc.sv $(RISCV_CORE)/memory_controller.sv $(RISCV_CORE)/RISCVBusiness.sv $(RISCV_CORE)/rv32i_reg_file.sv $(RISCV_CORE)/top_core.sv PIPELINE_FILES := $(PIPELINE)/tspp/tspp_execute_stage.sv $(PIPELINE)/tspp/tspp_fetch_stage.sv $(PIPELINE)/tspp/tspp_hazard_unit.sv #$(PIPELINE)/tspp/tspp.sv PREDICTOR_FILES := $(BRANCH_PREDICT)/branch_predictor_wrapper.sv $(BRANCH_PREDICT)/nottaken_predictor/nottaken_predictor.sv -PRIV_FILES := $(PRIVS)/priv_wrapper.sv $(PRIVS)/priv_1_12/priv_1_12_block.sv $(PRIVS)/priv_1_12/priv_1_12_int_ex_handler.sv $(PRIVS)/priv_1_12/priv_1_12_csr.sv $(PRIVS)/priv_1_12/priv_1_12_pipe_control.sv +PRIV_FILES := $(PRIVS)/priv_wrapper.sv $(PRIVS)/priv_1_12/priv_1_12_block.sv $(PRIVS)/priv_1_12/priv_1_12_int_ex_handler.sv $(PRIVS)/priv_1_12/priv_1_12_csr.sv $(PRIVS)/priv_1_12/priv_1_12_pipe_control.sv $(PRIVS)/priv_1_12/priv_1_12_pma.sv CACHE_FILES := $(CACHES)/caches_wrapper.sv $(CACHES)/pass_through/pass_through_cache.sv $(CACHES)/direct_mapped_tpf/direct_mapped_tpf_cache.sv $(CACHES)/separate_caches.sv SPARCE_FILES := $(SPARCE)/sparce_wrapper.sv $(SPARCE)/sparce_disabled/sparce_disabled.sv $(SPARCE)/sparce_enabled/sparce_cfid.sv $(SPARCE)/sparce_enabled/sparce_enabled.sv $(SPARCE)/sparce_enabled/sparce_psru.sv $(SPARCE)/sparce_enabled/sparce_sasa_table.sv $(SPARCE)/sparce_enabled/sparce_sprf.sv $(SPARCE)/sparce_enabled/sparce_svc.sv RISCV_BUS_FILES := $(RISCV_BUS)/generic_nonpipeline.sv $(RISCV_BUS)/ahb.sv diff --git a/source_code/include/priv_1_12_internal_if.vh b/source_code/include/priv_1_12_internal_if.vh index 0cddc0219..1495bec60 100644 --- a/source_code/include/priv_1_12_internal_if.vh +++ b/source_code/include/priv_1_12_internal_if.vh @@ -29,6 +29,7 @@ interface priv_1_12_internal_if; import machine_mode_types_1_12_pkg::*; + import pma_types_1_12_pkg::*; import rv32i_types_pkg::*; // RISC-MGMT? @@ -77,7 +78,13 @@ interface priv_1_12_internal_if; logic mret, sret, uret; // returning from a trap instruction logic intr; // Did something trigger an interrupt? + // Addresses and memory access info for memory protection + logic [RAM_ADDR_SIZE-1:0] daddr, iaddr; // Address to check + logic ren, wen, xen; // RWX access type + // PMA comm variables + pma_accwidth_t d_acc_width, i_acc_width; // Width of memory access + logic pma_s_fault, pma_l_fault, pma_i_fault; // PMA store fault, load fault, instruction fault modport csr ( input csr_addr, curr_priv, csr_write, csr_set, csr_clear, new_csr_val, inst_ret, valid_write, @@ -102,6 +109,11 @@ interface priv_1_12_internal_if; output insert_pc, priv_pc ); + modport pma ( + input iaddr, daddr, ren, wen, xen, d_acc_width, i_acc_width, + output pma_s_fault, pma_i_fault, pma_l_fault + ); + endinterface `endif // PRIV_1_12_INTERNAL_IF_VH diff --git a/source_code/include/prv_pipeline_if.vh b/source_code/include/prv_pipeline_if.vh index 57ccf0775..09dbc4d29 100644 --- a/source_code/include/prv_pipeline_if.vh +++ b/source_code/include/prv_pipeline_if.vh @@ -33,6 +33,7 @@ interface prv_pipeline_if(); import machine_mode_types_1_12_pkg::*; import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; // exception signals logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, @@ -59,8 +60,14 @@ interface prv_pipeline_if(); logic ex_rmgmt; logic [$clog2(`NUM_EXTENSIONS)-1:0] ex_rmgmt_cause; + // Memory protection signals + logic iren, dwen, dren; + logic [RAM_ADDR_SIZE-1:0] iaddr, daddr; + pma_accwidth_t d_acc_width, i_acc_width; + logic prot_fault_s, prot_fault_l, prot_fault_i; + modport hazard ( - input priv_pc, insert_pc, intr, + input priv_pc, insert_pc, intr, prot_fault_s, prot_fault_l, prot_fault_i, output pipe_clear, ret, epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, badaddr, wb_enable, @@ -68,10 +75,13 @@ interface prv_pipeline_if(); ); modport pipe ( - output swap, clr, set, wdata, csr_addr, valid_write, instr, - input rdata, invalid_csr + output swap, clr, set, wdata, csr_addr, valid_write, instr, dren, dwen, daddr, d_acc_width, + input rdata, invalid_csr, prot_fault_s, prot_fault_l ); + modport fetch ( + output iren, iaddr, i_acc_width + ); modport priv_block ( input pipe_clear, ret, epc, fault_insn, mal_insn, @@ -79,7 +89,10 @@ interface prv_pipeline_if(); breakpoint, env_m, badaddr, swap, clr, set, wdata, csr_addr, valid_write, wb_enable, instr, ex_rmgmt, ex_rmgmt_cause, - output priv_pc, insert_pc, intr, rdata, invalid_csr + daddr, iaddr, dren, dwen, iren, + d_acc_width, i_acc_width, + output priv_pc, insert_pc, intr, rdata, invalid_csr, + prot_fault_s, prot_fault_l, prot_fault_i ); endinterface diff --git a/source_code/packages/pma_types_1_12_pkg.sv b/source_code/packages/pma_types_1_12_pkg.sv new file mode 100644 index 000000000..f50e6b982 --- /dev/null +++ b/source_code/packages/pma_types_1_12_pkg.sv @@ -0,0 +1,78 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: pma_types_1_12_pkg.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 04/04/2022 +* Description: Types needed to implement physical memory attributes +*/ + +`ifndef PMA_TYPES_1_12_PKG_SV +`define PMA_TYPES_1_12_PKG_SV + +package pma_types_1_12_pkg; + + /* pmacfg types */ + + typedef enum logic [1:0] { + AMONone = 2'b00, + AMOSwap = 2'b01, + AMOLogical = 2'b10, + AMOArithmetic = 2'b11 + } pma_amo_t; + + typedef enum logic [1:0] { + RsrvNone = 2'b00, + RsrvNonEventual = 2'b01, + RsrvEventual = 2'b10, + RsrvReserved = 2'b11 + } pma_rsrv_t; + + typedef enum logic [2:0] { + ByteAcc = 3'b000, + HWLower = 3'b001, + HWUpper = 3'b010, + WordAcc = 3'b011, + Burst2W = 3'b100, + Burst4W = 3'b101, + Burst8W = 3'b110, + AccWidthReserved = 3'b111 + } pma_accwidth_t; + + typedef struct packed { + logic [1:0] reserved; + logic W; // Writes supported + logic R; // Reads supported + logic X; // Execute supported + pma_accwidth_t AccWidth; // Max supported access width + logic Idm; // Idempotency + logic Cache; // Able to cache + logic Coh; // Coherency + pma_rsrv_t Rsrv; // Reservability + pma_amo_t AMO; // AMO operations + logic MIO; // Memory or I/O + } pma_cfg_t; + + typedef struct packed { + pma_cfg_t pma_cfg_1; + pma_cfg_t pma_cfg_0; + } pma_reg_t; + +endpackage + +`endif //PMA_TYPES_1_12_PKG_SV diff --git a/source_code/packages/riscv_packages.core b/source_code/packages/riscv_packages.core index 362fef8b0..a059ee009 100644 --- a/source_code/packages/riscv_packages.core +++ b/source_code/packages/riscv_packages.core @@ -8,6 +8,7 @@ filesets: - alu_types_pkg.sv - rv32i_types_pkg.sv - machine_mode_types_1_12_pkg.sv + - pma_types_1_12_pkg.sv - risc_mgmt/crc32_pkg.sv - risc_mgmt/template_pkg.sv - risc_mgmt/test_pkg.sv diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index 06ede4efc..fb2af663d 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -52,6 +52,7 @@ module tspp_execute_stage ( ); import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; // Interface declarations control_unit_if cu_if (); @@ -243,8 +244,8 @@ module tspp_execute_stage ( // RISC-MGMT connection assign rm_if.mem_load = dgen_bus_if.rdata; - assign dgen_bus_if.ren = rm_if.req_mem ? rm_if.mem_ren : cu_if.dren & ~mal_addr; - assign dgen_bus_if.wen = rm_if.req_mem ? rm_if.mem_wen : cu_if.dwen & ~mal_addr; + assign dgen_bus_if.ren = rm_if.req_mem ? rm_if.mem_ren : cu_if.dren & ~mal_addr & ~prv_pipe_if.prot_fault_l; + assign dgen_bus_if.wen = rm_if.req_mem ? rm_if.mem_wen : cu_if.dwen & ~mal_addr & ~prv_pipe_if.prot_fault_s; assign byte_en_temp = rm_if.req_mem ? rm_if.mem_byte_en : byte_en_standard; assign dgen_bus_if.byte_en = byte_en; assign dgen_bus_if.addr = rm_if.req_mem ? rm_if.mem_addr : alu_if.port_out; @@ -389,6 +390,13 @@ module tspp_execute_stage ( assign hazard_if.epc_e = fetch_ex_if.fetch_ex_reg.pc; assign hazard_if.token_ex = fetch_ex_if.fetch_ex_reg.token; + // setup signals for memory protection + // not considering risc-mgmt here TODO + assign prv_pipe_if.dren = cu_if.dren & ~mal_addr; + assign prv_pipe_if.dwen = cu_if.dwen & ~mal_addr; + assign prv_pipe_if.daddr = alu_if.port_out; + assign prv_pipe_if.d_acc_width = WordAcc; + /********************************************************* *** Branch Predictor Logic *********************************************************/ diff --git a/source_code/pipelines/tspp/tspp_fetch_stage.sv b/source_code/pipelines/tspp/tspp_fetch_stage.sv index 1accd5354..5dac51c72 100644 --- a/source_code/pipelines/tspp/tspp_fetch_stage.sv +++ b/source_code/pipelines/tspp/tspp_fetch_stage.sv @@ -29,6 +29,7 @@ `include "component_selection_defines.vh" `include "cache_control_if.vh" `include "rv32c_if.vh" +`include "prv_pipeline_if.vh" module tspp_fetch_stage ( input logic CLK, @@ -38,9 +39,11 @@ module tspp_fetch_stage ( predictor_pipeline_if.access predict_if, generic_bus_if.cpu igen_bus_if, sparce_pipeline_if.pipe_fetch sparce_if, - rv32c_if.fetch rv32cif + rv32c_if.fetch rv32cif, + prv_pipeline_if.fetch prv_pipe_if ); import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; parameter logic [31:0] RESET_PC = 32'h80000000; @@ -110,6 +113,11 @@ module tspp_fetch_stage ( assign hazard_if.badaddr_f = igen_bus_if.addr; assign hazard_if.epc_f = pc; + // Send memory protection signals + assign prv_pipe_if.iren = igen_bus_if.ren; + assign prv_pipe_if.iaddr = igen_bus_if.addr; + assign prv_pipe_if.i_acc_width = WordAcc; + // Choose the endianness of the data coming into the processor generate if (BUS_ENDIANNESS == "big") assign instr = igen_bus_if.rdata; diff --git a/source_code/pipelines/tspp/tspp_hazard_unit.sv b/source_code/pipelines/tspp/tspp_hazard_unit.sv index 4d4f89164..ca27449fc 100644 --- a/source_code/pipelines/tspp/tspp_hazard_unit.sv +++ b/source_code/pipelines/tspp/tspp_hazard_unit.sv @@ -78,8 +78,8 @@ module tspp_hazard_unit ( assign prv_pipe_if.ret = hazard_if.ret; assign e_ex_stage = hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l | hazard_if.fault_s | hazard_if.mal_s | hazard_if.breakpoint | - hazard_if.env_m; - assign e_f_stage = hazard_if.fault_insn | hazard_if.mal_insn; + hazard_if.env_m | prv_pipe_if.prot_fault_l | prv_pipe_if.prot_fault_s; + assign e_f_stage = hazard_if.fault_insn | hazard_if.mal_insn | prv_pipe_if.prot_fault_i; assign intr = ~e_ex_stage & ~e_f_stage & prv_pipe_if.intr; assign prv_pipe_if.pipe_clear = e_ex_stage | ~(hazard_if.token_ex | rm_if.active_insn); @@ -95,12 +95,12 @@ module tspp_hazard_unit ( assign prv_pipe_if.wb_enable = !hazard_if.if_ex_stall | hazard_if.jump | hazard_if.branch; //Because 2 stages - assign prv_pipe_if.fault_insn = hazard_if.fault_insn; + assign prv_pipe_if.fault_insn = hazard_if.fault_insn | prv_pipe_if.prot_fault_i; assign prv_pipe_if.mal_insn = hazard_if.mal_insn; assign prv_pipe_if.illegal_insn = hazard_if.illegal_insn; - assign prv_pipe_if.fault_l = hazard_if.fault_l; + assign prv_pipe_if.fault_l = hazard_if.fault_l | prv_pipe_if.prot_fault_l; assign prv_pipe_if.mal_l = hazard_if.mal_l; - assign prv_pipe_if.fault_s = hazard_if.fault_s; + assign prv_pipe_if.fault_s = hazard_if.fault_s | prv_pipe_if.prot_fault_s; assign prv_pipe_if.mal_s = hazard_if.mal_s; assign prv_pipe_if.breakpoint = hazard_if.breakpoint; assign prv_pipe_if.env_m = hazard_if.env_m; diff --git a/source_code/privs/priv.core b/source_code/privs/priv.core index edb154f5a..f5acbc3ef 100644 --- a/source_code/privs/priv.core +++ b/source_code/privs/priv.core @@ -10,6 +10,7 @@ filesets: - priv_1_12/priv_1_12_block.sv - priv_1_12/priv_1_12_csr.sv - priv_1_12/priv_1_12_pipe_control.sv + - priv_1_12/priv_1_12_pma.sv file_type: systemVerilogSource diff --git a/source_code/privs/priv_1_12/priv_1_12_block.sv b/source_code/privs/priv_1_12/priv_1_12_block.sv index 7fac95f94..47b472e3b 100644 --- a/source_code/privs/priv_1_12/priv_1_12_block.sv +++ b/source_code/privs/priv_1_12/priv_1_12_block.sv @@ -25,6 +25,7 @@ `include "prv_pipeline_if.vh" `include "priv_1_12_internal_if.vh" `include "core_interrupt_if.vh" +`include "priv_ext_if.vh" module priv_1_12_block ( input logic CLK, nRST, @@ -35,10 +36,12 @@ module priv_1_12_block ( import machine_mode_types_1_12_pkg::*; priv_1_12_internal_if prv_intern_if(); + priv_ext_if priv_ext_pma_if(); - priv_1_12_csr csr (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if)); + priv_1_12_csr csr (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_pma_if(priv_ext_pma_if)); priv_1_12_int_ex_handler int_ex_handler (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if)); priv_1_12_pipe_control pipe_ctrl (.prv_intern_if(prv_intern_if)); + priv_1_12_pma pma (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_if(priv_ext_pma_if)); priv_level_t curr_priv; @@ -109,4 +112,16 @@ module priv_1_12_block ( assign prv_pipe_if.insert_pc = prv_intern_if.insert_pc; assign prv_pipe_if.intr = prv_intern_if.intr; + // Memory protection signals + assign prv_intern_if.daddr = prv_pipe_if.daddr; + assign prv_intern_if.iaddr = prv_pipe_if.iaddr; + assign prv_intern_if.d_acc_width = prv_pipe_if.d_acc_width; + assign prv_intern_if.i_acc_width = prv_pipe_if.i_acc_width; + assign prv_intern_if.ren = prv_pipe_if.dren; + assign prv_intern_if.wen = prv_pipe_if.dwen; + assign prv_intern_if.xen = prv_pipe_if.iren; + assign prv_pipe_if.prot_fault_i = prv_intern_if.pma_i_fault; + assign prv_pipe_if.prot_fault_l = prv_intern_if.pma_l_fault; + assign prv_pipe_if.prot_fault_s = prv_intern_if.pma_s_fault; + endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_csr.sv b/source_code/privs/priv_1_12/priv_1_12_csr.sv index 614eb84a4..62e081285 100644 --- a/source_code/privs/priv_1_12/priv_1_12_csr.sv +++ b/source_code/privs/priv_1_12/priv_1_12_csr.sv @@ -31,7 +31,8 @@ module priv_1_12_csr #( )( input CLK, input nRST, - priv_1_12_internal_if.csr prv_intern_if + priv_1_12_internal_if.csr prv_intern_if, + priv_ext_if.priv priv_ext_pma_if `ifdef RV32F_SUPPORTED , priv_ext_if.priv priv_ext_f_if `endif // RV32F_SUPPORTED @@ -79,19 +80,21 @@ module priv_1_12_csr #( assign prv_intern_if.invalid_csr = invalid_csr_0 | invalid_csr_1; // Extension Broadcast Signals -`ifdef RV32F_SUPPORTED + assign priv_ext_pma_if.csr_addr = prv_intern_if.csr_addr; + assign priv_ext_pma_if.value_in = nxt_csr_val; + assign priv_ext_pma_if.csr_active = ~invalid_csr_0 & prv_intern_if.valid_write + & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); + `ifdef RV32F_SUPPORTED assign priv_ext_f_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_f_if.value_in = nxt_csr_val; assign priv_ext_f_if.csr_active = prv_intern_if.valid_write - & (prv_intern_if.csr_write | prv_intern_if.csr_set - | prv_intern_if.csr_clear); + & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); `endif // RV32F_SUPPORTED `ifdef RV32V_SUPPORTED assign priv_ext_v_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_v_if.value_in = nxt_csr_val; assign priv_ext_v_if.csr_active = prv_intern_if.valid_write - & (prv_intern_if.csr_write | prv_intern_if.csr_set - | prv_intern_if.csr_clear); + & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); `endif // RV32V_SUPPORTED /* Save some logic with this */ @@ -362,6 +365,9 @@ module priv_1_12_csr #( MINSTRETH_ADDR: prv_intern_if.old_csr_val = minstreth; default: begin if (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear) begin + if (priv_ext_pma_if.ack) begin + prv_intern_if.old_csr_val = priv_ext_pma_if.value_out; + end `ifdef RV32F_SUPPORTED if (priv_ext_f_if.ack) begin prv_intern_if.old_csr_val = priv_ext_f_if.value_out; @@ -375,11 +381,12 @@ module priv_1_12_csr #( // CSR address doesn't exist invalid_csr_1 = 1'b1 + & (~priv_ext_pma_if.ack) & (~priv_ext_pma_if.invalid_csr) `ifdef RV32F_SUPPORTED - & (~priv_ext_f_if.ack) & (~priv_ext_f_if.invalid_csr) + & (~priv_ext_f_if.ack) & (~priv_ext_f_if.invalid_csr) `endif // RV32F_SUPPORTED `ifdef RV32V_SUPPORTED - & (~priv_ext_v_if.ack) & (~priv_ext_v_if.invalid_csr) + & (~priv_ext_v_if.ack) & (~priv_ext_v_if.invalid_csr) `endif // RV32V_SUPPORTED ; end diff --git a/source_code/privs/priv_1_12/priv_1_12_pma.sv b/source_code/privs/priv_1_12/priv_1_12_pma.sv new file mode 100644 index 000000000..dc16e7690 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pma.sv @@ -0,0 +1,126 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_pma.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 04/05/2022 +* Description: PMA Checker, version 1.12 +*/ + +`include "priv_1_12_internal_if.vh" +`include "priv_ext_if.vh" + +module priv_1_12_pma ( + input logic CLK, nRST, + priv_1_12_internal_if.pma prv_intern_if, + priv_ext_if.ext priv_ext_if +); + + import pma_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + pma_reg_t [15:0] pma_regs, nxt_pma_regs; + pma_reg_t active_reg_d, active_reg_i; + pma_cfg_t pma_cfg_d, pma_cfg_i; + pma_reg_t new_val; + + // Core State Registers + always_ff @ (posedge CLK, negedge nRST) begin + if (~nRST) begin + pma_regs[00] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h1BF1)}); // 0 - ROM, 1 - RAM + pma_regs[01] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM + pma_regs[02] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM + pma_regs[03] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM + pma_regs[04] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM + pma_regs[05] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM + pma_regs[06] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM + pma_regs[07] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM + pma_regs[08] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O + pma_regs[09] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O + pma_regs[10] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O + pma_regs[11] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O + pma_regs[12] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O + pma_regs[13] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O + pma_regs[14] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O + pma_regs[15] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O + end else begin + pma_regs <= nxt_pma_regs; + end + end + + // Core State Logic + always_comb begin + nxt_pma_regs = pma_regs; + priv_ext_if.ack = 1'b0; + new_val = pma_reg_t'(priv_ext_if.value_in); + if (priv_ext_if.csr_addr[11:4] == 8'b10111100) begin + priv_ext_if.ack = 1'b1; + if (priv_ext_if.csr_active) begin + // WARL checks + if (new_val.pma_cfg_0.Rsrv == RsrvReserved) begin + new_val.pma_cfg_0.Rsrv = RsrvNone; + end + if (new_val.pma_cfg_1.Rsrv == RsrvReserved) begin + new_val.pma_cfg_0.Rsrv = RsrvNone; + end + if (new_val.pma_cfg_0.AccWidth == AccWidthReserved) begin + new_val.pma_cfg_0.Rsrv = WordAcc; + end + if (new_val.pma_cfg_1.AccWidth == AccWidthReserved) begin + new_val.pma_cfg_0.Rsrv = WordAcc; + end + + nxt_pma_regs[priv_ext_if.csr_addr[3:0]] = new_val; + end + end + end + + assign priv_ext_if.invalid_csr = 1'b0; + assign priv_ext_if.value_out = pma_regs[priv_ext_if.csr_addr[3:0]]; + + // PMA Logic Block + always_comb begin + prv_intern_if.pma_l_fault = 1'b0; + prv_intern_if.pma_s_fault = 1'b0; + prv_intern_if.pma_i_fault = 1'b0; + + active_reg_d = pma_regs[prv_intern_if.daddr[31:28]]; + active_reg_i = pma_regs[prv_intern_if.iaddr[31:28]]; + + if (~prv_intern_if.daddr[27]) begin + pma_cfg_d = active_reg_d.pma_cfg_0; + end else begin + pma_cfg_d = active_reg_d.pma_cfg_1; + end + + if (~prv_intern_if.iaddr[27]) begin + pma_cfg_i = active_reg_i.pma_cfg_0; + end else begin + pma_cfg_i = active_reg_i.pma_cfg_1; + end + + if (prv_intern_if.ren & (~pma_cfg_d.R || (prv_intern_if.d_acc_width > pma_cfg_d.AccWidth))) begin + prv_intern_if.pma_l_fault = 1'b1; + end else if (prv_intern_if.wen & (~pma_cfg_d.W || (prv_intern_if.d_acc_width > pma_cfg_d.AccWidth))) begin + prv_intern_if.pma_s_fault = 1'b1; + end else if (prv_intern_if.xen & (~pma_cfg_i.X || (prv_intern_if.i_acc_width > pma_cfg_i.AccWidth))) begin + prv_intern_if.pma_i_fault = 1'b1; + end + end + +endmodule \ No newline at end of file diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index 17085272d..a639296b0 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -94,7 +94,8 @@ module RISCVBusiness ( .predict_if(predict_if), .igen_bus_if(tspp_icache_gen_bus_if), .sparce_if(sparce_if), - .rv32cif(rv32cif) + .rv32cif(rv32cif), + .prv_pipe_if(prv_pipe_if) ); tspp_execute_stage execute_stage_i ( diff --git a/source_code/tb/tb_priv_1_12_block.sv b/source_code/tb/tb_priv_1_12_block.sv index ba8021b8f..f56ec56a8 100644 --- a/source_code/tb/tb_priv_1_12_block.sv +++ b/source_code/tb/tb_priv_1_12_block.sv @@ -139,7 +139,7 @@ module tb_priv_1_12_block (); if (prv_pipeline_if.rdata == 32'b0) $display("> Test %d: PASS", test_num); else - $display("> Test %d: FAIL (got %b) (expected %b)", test_num, prv_pipeline_if.rdata, 32'b0); + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); @(posedge CLK); #PROP_DELAY; test_num++; @@ -153,7 +153,7 @@ module tb_priv_1_12_block (); if (prv_pipeline_if.rdata == 32'b0) $display("> Test %d: PASS", test_num); else - $display("> Test %d: FAIL (got %b) (expected %b)", test_num, prv_pipeline_if.rdata, 32'b0); + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); @(posedge CLK); #PROP_DELAY; test_num++; @@ -167,7 +167,7 @@ module tb_priv_1_12_block (); if (prv_pipeline_if.rdata == 32'b0) $display("> Test %d: PASS", test_num); else - $display("> Test %d: FAIL (got %b) (expected %b)", test_num, prv_pipeline_if.rdata, 32'b0); + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); @(posedge CLK); #PROP_DELAY; test_num++; @@ -181,7 +181,7 @@ module tb_priv_1_12_block (); if (prv_pipeline_if.rdata == 32'b0) $display("> Test %d: PASS", test_num); else - $display("> Test %d: FAIL (got %b) (expected %b)", test_num, prv_pipeline_if.rdata, 32'b0); + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); @(posedge CLK); #PROP_DELAY; test_num++; diff --git a/tb_core.cc b/tb_core.cc index b89154b79..1c8d568d6 100644 --- a/tb_core.cc +++ b/tb_core.cc @@ -10,14 +10,14 @@ #include "Vtop_core.h" #include "Vtop_core_top_core.h" -#define MTIME_ADDR 0x10000 -#define MTIMEH_ADDR 0x10004 -#define MTIMECMP_ADDR 0x10008 -#define MTIMECMPH_ADDR 0x1000C -#define MSIP_ADDR 0x10010 -#define EXT_ADDR_SET 0x10014 -#define EXT_ADDR_CLEAR 0x10018 -#define MAGIC_ADDR 0x20000 +#define MTIME_ADDR 0xFFFFFFE0 +#define MTIMEH_ADDR 0xFFFFFFE4 +#define MTIMECMP_ADDR 0xFFFFFFE8 +#define MTIMECMPH_ADDR 0xFFFFFFEC +#define MSIP_ADDR 0xFFFFFFF0 +#define EXT_ADDR_SET 0xFFFFFFF4 +#define EXT_ADDR_CLEAR 0xFFFFFFF8 +#define MAGIC_ADDR 0xFFFFFFFC // Inclusive range of memory-mapped peripherals #define MMIO_RANGE_BEGIN (MTIME_ADDR) diff --git a/verification/interrupt-tests/ext_interrupt.S b/verification/interrupt-tests/ext_interrupt.S deleted file mode 100644 index 87a321d48..000000000 --- a/verification/interrupt-tests/ext_interrupt.S +++ /dev/null @@ -1,171 +0,0 @@ -/* -* Copyright 2016 Purdue University -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* -* Filename: ext_interrupt.S -* -* Created by: Enes Shaltami -* Email: ashaltam@purdue.edu -* Date Created: 03/15/2020 -* Description: Test of external interrupts -*/ - -#include "riscv_test.h" -#include "test_macros.h" - -#define claim_complete_reg 0x80040018 - -RVTEST_RV32U -RVTEST_CODE_BEGIN - - -main: - la t0, mtvec_handler // load in address of mtvec_handler - ori t0, t0, 0x1 // or in the MODE (Vectored) - li TESTNUM, 0 // initialize TESTNUM (x28) - csrw mtvec, t0 // assign the supported vector handler into mtvec - - addi x3, x0, 0x1 - slli x3, x3, 11 // store 0x800 into x3 and x4 - csrw mie, x3 // Write value of 0x800 into mie, external interrupt enable for M-mode - - ori x4, x0, 0x1 - slli x4, x4, 3 - csrw mstatus, x4 // turn on the mie bit within mstatus - -idle_loop: - beq TESTNUM, x0, idle_loop // infinite loop will only finish once the external interrupt ISR has completed properly - - j done // jump to outside subroutine which will halt - //RVTEST_PASS - - -.align 2 -mtvec_handler: // General vector table for all exceptions and interrupts - // Interrupts must be disabled at entry to handler, could have jumps to different parts of code, potentially -IRQ_0: - j exception_handler // NOTE: PC for exception handler and user software interrupt handler are the same -IRQ_1: - j supervisor_software_interrupt_handler -IRQ_2: - j reserved_software_interrupt_handler -IRQ_3: - j machine_software_interrupt_handler -IRQ_4: - j user_timer_interrupt_handler -IRQ_5: - j supervisor_timer_interrupt_handler -IRQ_6: - j reserved_timer_interrupt_handler -IRQ_7: - j machine_timer_interrupt_handler -IRQ_8: - j user_external_interrupt_handler -IRQ_9: - j supervisor_external_interrupt_handler -IRQ_10: - j reserved_external_interrupt_handler -IRQ_11: - j machine_external_interrupt_handler - -exception_handler: // manage all exceptions here -supervisor_software_interrupt_handler: -reserved_software_interrupt_handler: -machine_software_interrupt_handler: -user_timer_interrupt_handler: -supervisor_timer_interrupt_handler: -reserved_timer_interrupt_handler: -machine_timer_interrupt_handler: -user_external_interrupt_handler: -supervisor_external_interrupt_handler: -reserved_external_interrupt_handler: - jal x1, push_registers - nop - jal x1, pop_registers - mret - -machine_external_interrupt_handler: - //jal x1, push_registers - addi x16, x0, 16 - addi x17, x0, 17 - addi x18, x0, 18 - addi x19, x0, 19 - addi x20, x0, 20 - - //la x2, claim_complete_reg // read the claim complete register to know the type of interrupt that occurred - lui x2, 0x80040 // load in the claim complete register address - addi x2, x2, 0x018 - lw x3, 0(x2) - beq x0, x3, invalid_id_read - li TESTNUM, 1 - sw x3, 0(x2) - -invalid_id_read: - //jal x1, pop_registers - mret - -push_registers: - // push RISC-V ABI registers - lui x2, 0x8 - ori x2, x2, 0x3FC - addi x2, x2, -64 // stack pointer - //sw x1, 4(x2) // return address - sw x5, 8(x2) // temporary registers - sw x6, 12(x2) - sw x7, 16(x2) - //sw x28, 20(x2) // disclude TESTNUM - sw x29, 24(x2) - sw x30, 28(x2) - sw x31, 32(x2) - sw x10, 36(x2) // function arguments - sw x11, 40(x2) - sw x12, 44(x2) - sw x13, 48(x2) - sw x14, 52(x2) - sw x15, 56(x2) - sw x16, 60(x2) - sw x17, 64(x2) - jr x1 - -pop_registers: - // pop registers - //lw x1, 4(x2) - lw x5, 8(x2) - lw x6, 12(x2) - lw x7, 16(x2) - //lw x28, 20(x2) - lw x29, 24(x2) - lw x30, 28(x2) - lw x31, 32(x2) - lw x10, 36(x2) - lw x11, 40(x2) - lw x12, 44(x2) - lw x13, 48(x2) - lw x14, 52(x2) - lw x15, 56(x2) - lw x16, 60(x2) - lw x17, 64(x2) - addi x2, x2, 64 - jr x1 - - - -RVTEST_CODE_END - -.data -RVTEST_DATA_BEGIN - TEST_DATA -RVTEST_DATA_END - diff --git a/verification/interrupt-tests/interrupts.S b/verification/interrupt-tests/interrupts.S deleted file mode 100644 index a0bcd29f0..000000000 --- a/verification/interrupt-tests/interrupts.S +++ /dev/null @@ -1,52 +0,0 @@ -#include "riscv_test.h" -#include "test_macros.h" - -RVTEST_RV32U - -RVTEST_CODE_BEGIN - -main: - # Set up handler address - la t0, mtvec_handler - csrw mtvec, t0 - - # Enable Interrupts globally - addi x3, x0, 0x1 - slli x3, x3, 3 - csrw mstatus, x3 - - # Enable timer, software, external interrupts for machine mode - ori x3, x0, 0x8 - slli x3, x3, 8 - ori x3, x3, 0x88 - csrw mie, x3 - - # Load in magic sum: 3 + 7 + 11 corresponds to the values of the cause - # register for machine software, timer, and external interrupts respectively. - # This test will only pass if the 3 types are seen exactly once. - ori x6, x0, 21 - -loop: - # Wait for x6 to take on correct value - bne x6, x0, loop - RVTEST_PASS - -.align 2 -mtvec_handler: - csrr x7, mcause - andi x7, x7, 0xFF # Mask lower bits -- top bit indicates interrupt vs. exception - sub x6, x6, x7 - - # Reenable interrupts - #addi x3, x0, 0x1 - #slli x3, x3, 3 - #csrw mstatus, x3 - - mret - -RVTEST_CODE_END - -.data -RVTEST_DATA_BEGIN - TEST_DATA -RVTEST_DATA_END diff --git a/verification/interrupt-tests/soft_interrupt.S b/verification/interrupt-tests/soft_interrupt.S deleted file mode 100644 index 8f044b37e..000000000 --- a/verification/interrupt-tests/soft_interrupt.S +++ /dev/null @@ -1,79 +0,0 @@ -/* -* Copyright 2016 Purdue University -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* -* Filename: timer2.S -* -* Created by: John Skubic -* Email: jskubic69@purdue.edu -* Date Created: 09/27/2016 -* Description: Test of returning from interrupts, make sure every insn is -* executed and none are executed twice -*/ - -#include "riscv_test.h" -#include "test_macros.h" - - -RVTEST_RV32U - - -RVTEST_CODE_BEGIN - -.equ msip, 0x80050000 - -main: - # Define trap vector address - la t0, mtvec_handler - csrw mtvec, t0 - - addi x6, x0, 0xFF - addi x7, x0, 0xFF - - addi x3, x0, 0x1 - slli x3, x3, 3 - csrw mstatus, x3 - - ori x3, x0, 0x8 - csrw mie, x3 - - la x1, msip - lw x2, 0(x1) - ori x2, x0, 1 - sw x2, 0(x1) - -idle_loop: - beq x6, x7, idle_loop - - RVTEST_PASS - -.align 2 -mtvec_handler: - # Write to mtimecmp using the zero register - # Check that nothing happened to interrupt - # Actually write to mtimecmp - # Check that the interrupt cleared - # Change sigil to show interrupt was entered - ori x6, x0, 0x0F - # Return - mret - -RVTEST_CODE_END - -.data -RVTEST_DATA_BEGIN - TEST_DATA -RVTEST_DATA_END - diff --git a/verification/interrupts-exceptions/utility.h b/verification/interrupts-exceptions/utility.h index 9229d8017..6caaee7c5 100644 --- a/verification/interrupts-exceptions/utility.h +++ b/verification/interrupts-exceptions/utility.h @@ -3,14 +3,14 @@ #include - #define MTIME_ADDR 0x10000 - #define MTIMEH_ADDR 0x10004 - #define MTIMECMP_ADDR 0x10008 - #define MTIMECMPH_ADDR 0x1000C - #define MSIP_ADDR 0x10010 - #define EXT_ADDR_SET 0x10014 - #define EXT_ADDR_CLEAR 0x10018 - #define MAGIC_ADDR 0x20000 + #define MTIME_ADDR 0xFFFFFFE0 + #define MTIMEH_ADDR 0xFFFFFFE4 + #define MTIMECMP_ADDR 0xFFFFFFE8 + #define MTIMECMPH_ADDR 0xFFFFFFEC + #define MSIP_ADDR 0xFFFFFFF0 + #define EXT_ADDR_SET 0xFFFFFFF4 + #define EXT_ADDR_CLEAR 0xFFFFFFF8 + #define MAGIC_ADDR 0xFFFFFFFC void print(char *string); diff --git a/verification/pma-tests/build_all.py b/verification/pma-tests/build_all.py new file mode 100755 index 000000000..f306e6c5e --- /dev/null +++ b/verification/pma-tests/build_all.py @@ -0,0 +1,51 @@ +#! /usr/bin/python3 + +import subprocess +import glob +import os +import pathlib + + +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i', '-mabi=ilp32', '-mcmodel=medany', + '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] + +cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] + + +if not os.path.isfile('./start.S') or not os.path.isfile('link.ld'): + print('Error: Could not find AFTx06.S or link.ld in this directory') + exit(1) + + +for fname in (glob.glob('./*.c') + glob.glob('./*.S')): + if 'start' in fname or 'utility' in fname: + print("Skipping {} as top-level file, appears to be a utility".format(fname)); + continue + print('Compiling {}'.format(fname)) + basename = pathlib.Path(fname).stem + + rv = subprocess.run(compile_cmd + [fname, '-o', basename + '.elf'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + print('Converting {} to binary'.format(fname)) + rv = subprocess.run(cvt_cmd + [basename + '.elf', basename + '.bin']) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + +print( +''' + Finished compilation. Now, pass the '.bin' file corresponding to + the example to run as an argument to 'VbASIC_wrapper' to run an example! +''') diff --git a/verification/pma-tests/link.ld b/verification/pma-tests/link.ld new file mode 100755 index 000000000..4fd20ea69 --- /dev/null +++ b/verification/pma-tests/link.ld @@ -0,0 +1,14 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x0000000080000000; + .text.init : { *(.text.init) } + .text : { *(.text) } + .data ALIGN(0x1000) : { *(.data) } + .bss : { *(.bss) } + . = 0x0000000080007000; + .statuses : { *(.statuses) } + _end = .; +} diff --git a/verification/pma-tests/pma.c b/verification/pma-tests/pma.c new file mode 100644 index 000000000..7032a6ec9 --- /dev/null +++ b/verification/pma-tests/pma.c @@ -0,0 +1,37 @@ +#include +#include "utility.h" + +extern volatile int flag; + +#define PMA_ROM_ADDR 0x100 +#define PMA_RAM_ADDR 0x20000000 +volatile uint32_t *pma_rom_addr = (uint32_t*) PMA_ROM_ADDR; +volatile uint32_t *pma_ram_addr = (uint32_t*) PMA_RAM_ADDR; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + print("PMA Checker failed (expected)\n"); + flag = 2; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + uint32_t mstatus_value = 0x8; + + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + // This should fail + *pma_rom_addr = 0xDEADBEEF; + + // This should succeed + *pma_ram_addr = 0xDEADBEEF; + flag -= 1; + + return 0; +} diff --git a/verification/pma-tests/start.S b/verification/pma-tests/start.S new file mode 100644 index 000000000..9e588dd07 --- /dev/null +++ b/verification/pma-tests/start.S @@ -0,0 +1,17 @@ + +.global _start +_start: + li sp, 0x8000F000 + jal main + +.global done +done: + la x28, flag + lw x28, 0(x28) +__inf_loop: + j __inf_loop + + +.global flag +.data +flag: .word 0xFF diff --git a/verification/pma-tests/utility.c b/verification/pma-tests/utility.c new file mode 100644 index 000000000..d89404f7e --- /dev/null +++ b/verification/pma-tests/utility.c @@ -0,0 +1,25 @@ + +#include "utility.h" + +void print(char *string) { + volatile char *magic = (volatile char *)MAGIC_ADDR; + + for(int i = 0; string[i]; i++) { + (*magic) = string[i]; + } +} + +void put_uint32_hex(uint32_t x) { + char buf[10] = {0}; + + for(int i = 0; i < 8; i++) { + uint8_t value = (x & 0xF); + if(value >= 10) { + buf[7-i] = (value + 'A'); + } else { + buf[7-i] = (value + '0'); + } + x >>= 4; + } + print(buf); +} diff --git a/verification/pma-tests/utility.h b/verification/pma-tests/utility.h new file mode 100644 index 000000000..6caaee7c5 --- /dev/null +++ b/verification/pma-tests/utility.h @@ -0,0 +1,19 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + + #include + + #define MTIME_ADDR 0xFFFFFFE0 + #define MTIMEH_ADDR 0xFFFFFFE4 + #define MTIMECMP_ADDR 0xFFFFFFE8 + #define MTIMECMPH_ADDR 0xFFFFFFEC + #define MSIP_ADDR 0xFFFFFFF0 + #define EXT_ADDR_SET 0xFFFFFFF4 + #define EXT_ADDR_CLEAR 0xFFFFFFF8 + #define MAGIC_ADDR 0xFFFFFFFC + + + void print(char *string); + void put_uint32_hex(uint32_t hex); + +#endif From 03e4fb5a21df6e7992749b986206daa767ae118a Mon Sep 17 00:00:00 2001 From: Hadi Ahmed Date: Tue, 15 Nov 2022 21:29:19 -0500 Subject: [PATCH 17/47] v1.12 PMP Integration (#22) Implementation of Priv 1.12 PMP --- source_code/include/priv_1_12_internal_if.vh | 11 +- source_code/include/prv_pipeline_if.vh | 6 +- source_code/packages/pmp_types_1_12_pkg.sv | 138 ++++----- source_code/packages/riscv_packages.core | 1 + .../pipelines/tspp/tspp_execute_stage.sv | 4 +- .../pipelines/tspp/tspp_fetch_stage.sv | 6 +- .../pipelines/tspp/tspp_hazard_unit.sv | 10 +- source_code/privs/priv.core | 2 + .../privs/priv_1_12/priv_1_12_block.sv | 10 +- source_code/privs/priv_1_12/priv_1_12_csr.sv | 25 +- .../priv_1_12/priv_1_12_int_ex_handler.sv | 6 +- source_code/privs/priv_1_12/priv_1_12_pma.sv | 44 +-- source_code/privs/priv_1_12/priv_1_12_pmp.sv | 272 ++++++++++++++++++ .../privs/priv_1_12/priv_1_12_pmp_matcher.py | 14 + .../privs/priv_1_12/priv_1_12_pmp_matcher.sv | 98 +++++++ verification/pmp-tests/build_all.py | 51 ++++ verification/pmp-tests/link.ld | 14 + verification/pmp-tests/pmp_csr.c | 137 +++++++++ verification/pmp-tests/pmp_na4.c | 54 ++++ verification/pmp-tests/pmp_napot.c | 63 ++++ verification/pmp-tests/pmp_tor.c | 67 +++++ verification/pmp-tests/start.S | 17 ++ verification/pmp-tests/utility.c | 26 ++ verification/pmp-tests/utility.h | 19 ++ 24 files changed, 979 insertions(+), 116 deletions(-) create mode 100644 source_code/privs/priv_1_12/priv_1_12_pmp.sv create mode 100644 source_code/privs/priv_1_12/priv_1_12_pmp_matcher.py create mode 100644 source_code/privs/priv_1_12/priv_1_12_pmp_matcher.sv create mode 100755 verification/pmp-tests/build_all.py create mode 100755 verification/pmp-tests/link.ld create mode 100644 verification/pmp-tests/pmp_csr.c create mode 100644 verification/pmp-tests/pmp_na4.c create mode 100644 verification/pmp-tests/pmp_napot.c create mode 100644 verification/pmp-tests/pmp_tor.c create mode 100644 verification/pmp-tests/start.S create mode 100644 verification/pmp-tests/utility.c create mode 100644 verification/pmp-tests/utility.h diff --git a/source_code/include/priv_1_12_internal_if.vh b/source_code/include/priv_1_12_internal_if.vh index 1495bec60..11d482a57 100644 --- a/source_code/include/priv_1_12_internal_if.vh +++ b/source_code/include/priv_1_12_internal_if.vh @@ -37,9 +37,10 @@ interface priv_1_12_internal_if; logic ex_rmgmt; logic [$clog2(`NUM_EXTENSIONS)-1:0] ex_rmgmt_cause; + priv_level_t curr_priv; // Current process privilege + // CSR block values csr_addr_t csr_addr; // CSR address to read - priv_level_t curr_priv; // Current process privilege logic csr_write, csr_set, csr_clear; // Is the CSR currently being modified? logic invalid_csr; // Bad CSR address logic inst_ret; // signal when an instruction is retired @@ -86,6 +87,9 @@ interface priv_1_12_internal_if; pma_accwidth_t d_acc_width, i_acc_width; // Width of memory access logic pma_s_fault, pma_l_fault, pma_i_fault; // PMA store fault, load fault, instruction fault + // PMP variables + logic pmp_s_fault, pmp_l_fault, pmp_i_fault; // PMP store fault, load fault, instruction fault + modport csr ( input csr_addr, curr_priv, csr_write, csr_set, csr_clear, new_csr_val, inst_ret, valid_write, inject_mcause, inject_mepc, inject_mie, inject_mip, inject_mstatus, inject_mtval, @@ -114,6 +118,11 @@ interface priv_1_12_internal_if; output pma_s_fault, pma_i_fault, pma_l_fault ); + modport pmp ( + input iaddr, daddr, ren, wen, xen, curr_priv, curr_mstatus, + output pmp_s_fault, pmp_i_fault, pmp_l_fault + ); + endinterface `endif // PRIV_1_12_INTERNAL_IF_VH diff --git a/source_code/include/prv_pipeline_if.vh b/source_code/include/prv_pipeline_if.vh index 09dbc4d29..52657b45e 100644 --- a/source_code/include/prv_pipeline_if.vh +++ b/source_code/include/prv_pipeline_if.vh @@ -20,9 +20,6 @@ * Email: jskubic@purdue.edu * Date Created: 08/24/2016 * Description: Interface connecting the priv block to the pipeline. -* Contains connections between modules inside the priv block. -* TODO: These two functionalities should be split into two -* separate interfaces. */ `ifndef PRV_PIPELINE_IF_VH @@ -67,7 +64,7 @@ interface prv_pipeline_if(); logic prot_fault_s, prot_fault_l, prot_fault_i; modport hazard ( - input priv_pc, insert_pc, intr, prot_fault_s, prot_fault_l, prot_fault_i, + input priv_pc, insert_pc, intr, output pipe_clear, ret, epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, badaddr, wb_enable, @@ -80,6 +77,7 @@ interface prv_pipeline_if(); ); modport fetch ( + input prot_fault_i, output iren, iaddr, i_acc_width ); diff --git a/source_code/packages/pmp_types_1_12_pkg.sv b/source_code/packages/pmp_types_1_12_pkg.sv index 64a48cc8a..af061a0ac 100644 --- a/source_code/packages/pmp_types_1_12_pkg.sv +++ b/source_code/packages/pmp_types_1_12_pkg.sv @@ -34,18 +34,18 @@ package pmp_types_1_12_pkg; PMPCFG1_ADDR = 12'h3A1, PMPCFG2_ADDR = 12'h3A2, PMPCFG3_ADDR = 12'h3A3, - PMPCFG4_ADDR = 12'h3A4, - PMPCFG5_ADDR = 12'h3A5, - PMPCFG6_ADDR = 12'h3A6, - PMPCFG7_ADDR = 12'h3A7, - PMPCFG8_ADDR = 12'h3A8, - PMPCFG9_ADDR = 12'h3A9, - PMPCFG10_ADDR = 12'h3AA, - PMPCFG11_ADDR = 12'h3AB, - PMPCFG12_ADDR = 12'h3AC, - PMPCFG13_ADDR = 12'h3AD, - PMPCFG14_ADDR = 12'h3AE, - PMPCFG15_ADDR = 12'h3AF, + // PMPCFG4_ADDR = 12'h3A4, + // PMPCFG5_ADDR = 12'h3A5, + // PMPCFG6_ADDR = 12'h3A6, + // PMPCFG7_ADDR = 12'h3A7, + // PMPCFG8_ADDR = 12'h3A8, + // PMPCFG9_ADDR = 12'h3A9, + // PMPCFG10_ADDR = 12'h3AA, + // PMPCFG11_ADDR = 12'h3AB, + // PMPCFG12_ADDR = 12'h3AC, + // PMPCFG13_ADDR = 12'h3AD, + // PMPCFG14_ADDR = 12'h3AE, + // PMPCFG15_ADDR = 12'h3AF, PMPADDR0_ADDR = 12'h3B0, PMPADDR1_ADDR = 12'h3B1, PMPADDR2_ADDR = 12'h3B2, @@ -61,56 +61,56 @@ package pmp_types_1_12_pkg; PMPADDR12_ADDR = 12'h3BC, PMPADDR13_ADDR = 12'h3BD, PMPADDR14_ADDR = 12'h3BE, - PMPADDR15_ADDR = 12'h3BF, - PMPADDR16_ADDR = 12'h3C0, - PMPADDR17_ADDR = 12'h3C1, - PMPADDR18_ADDR = 12'h3C2, - PMPADDR19_ADDR = 12'h3C3, - PMPADDR20_ADDR = 12'h3C4, - PMPADDR21_ADDR = 12'h3C5, - PMPADDR22_ADDR = 12'h3C6, - PMPADDR23_ADDR = 12'h3C7, - PMPADDR24_ADDR = 12'h3C8, - PMPADDR25_ADDR = 12'h3C9, - PMPADDR26_ADDR = 12'h3CA, - PMPADDR27_ADDR = 12'h3CB, - PMPADDR28_ADDR = 12'h3CC, - PMPADDR29_ADDR = 12'h3CD, - PMPADDR30_ADDR = 12'h3CE, - PMPADDR31_ADDR = 12'h3CF, - PMPADDR32_ADDR = 12'h3D0, - PMPADDR33_ADDR = 12'h3D1, - PMPADDR34_ADDR = 12'h3D2, - PMPADDR35_ADDR = 12'h3D3, - PMPADDR36_ADDR = 12'h3D4, - PMPADDR37_ADDR = 12'h3D5, - PMPADDR38_ADDR = 12'h3D6, - PMPADDR39_ADDR = 12'h3D7, - PMPADDR40_ADDR = 12'h3D8, - PMPADDR41_ADDR = 12'h3D9, - PMPADDR42_ADDR = 12'h3DA, - PMPADDR43_ADDR = 12'h3DB, - PMPADDR44_ADDR = 12'h3DC, - PMPADDR45_ADDR = 12'h3DD, - PMPADDR46_ADDR = 12'h3DE, - PMPADDR47_ADDR = 12'h3DF, - PMPADDR48_ADDR = 12'h3E0, - PMPADDR49_ADDR = 12'h3E1, - PMPADDR50_ADDR = 12'h3E2, - PMPADDR51_ADDR = 12'h3E3, - PMPADDR52_ADDR = 12'h3E4, - PMPADDR53_ADDR = 12'h3E5, - PMPADDR54_ADDR = 12'h3E6, - PMPADDR55_ADDR = 12'h3E7, - PMPADDR56_ADDR = 12'h3E8, - PMPADDR57_ADDR = 12'h3E9, - PMPADDR58_ADDR = 12'h3EA, - PMPADDR59_ADDR = 12'h3EB, - PMPADDR60_ADDR = 12'h3EC, - PMPADDR61_ADDR = 12'h3ED, - PMPADDR62_ADDR = 12'h3EE, - PMPADDR63_ADDR = 12'h3EF - } pmp_addr_t; + PMPADDR15_ADDR = 12'h3BF + // PMPADDR16_ADDR = 12'h3C0, + // PMPADDR17_ADDR = 12'h3C1, + // PMPADDR18_ADDR = 12'h3C2, + // PMPADDR19_ADDR = 12'h3C3, + // PMPADDR20_ADDR = 12'h3C4, + // PMPADDR21_ADDR = 12'h3C5, + // PMPADDR22_ADDR = 12'h3C6, + // PMPADDR23_ADDR = 12'h3C7, + // PMPADDR24_ADDR = 12'h3C8, + // PMPADDR25_ADDR = 12'h3C9, + // PMPADDR26_ADDR = 12'h3CA, + // PMPADDR27_ADDR = 12'h3CB, + // PMPADDR28_ADDR = 12'h3CC, + // PMPADDR29_ADDR = 12'h3CD, + // PMPADDR30_ADDR = 12'h3CE, + // PMPADDR31_ADDR = 12'h3CF, + // PMPADDR32_ADDR = 12'h3D0, + // PMPADDR33_ADDR = 12'h3D1, + // PMPADDR34_ADDR = 12'h3D2, + // PMPADDR35_ADDR = 12'h3D3, + // PMPADDR36_ADDR = 12'h3D4, + // PMPADDR37_ADDR = 12'h3D5, + // PMPADDR38_ADDR = 12'h3D6, + // PMPADDR39_ADDR = 12'h3D7, + // PMPADDR40_ADDR = 12'h3D8, + // PMPADDR41_ADDR = 12'h3D9, + // PMPADDR42_ADDR = 12'h3DA, + // PMPADDR43_ADDR = 12'h3DB, + // PMPADDR44_ADDR = 12'h3DC, + // PMPADDR45_ADDR = 12'h3DD, + // PMPADDR46_ADDR = 12'h3DE, + // PMPADDR47_ADDR = 12'h3DF, + // PMPADDR48_ADDR = 12'h3E0, + // PMPADDR49_ADDR = 12'h3E1, + // PMPADDR50_ADDR = 12'h3E2, + // PMPADDR51_ADDR = 12'h3E3, + // PMPADDR52_ADDR = 12'h3E4, + // PMPADDR53_ADDR = 12'h3E5, + // PMPADDR54_ADDR = 12'h3E6, + // PMPADDR55_ADDR = 12'h3E7, + // PMPADDR56_ADDR = 12'h3E8, + // PMPADDR57_ADDR = 12'h3E9, + // PMPADDR58_ADDR = 12'h3EA, + // PMPADDR59_ADDR = 12'h3EB, + // PMPADDR60_ADDR = 12'h3EC, + // PMPADDR61_ADDR = 12'h3ED, + // PMPADDR62_ADDR = 12'h3EE, + // PMPADDR63_ADDR = 12'h3EF + } pmp_csr_addr_t; /* pmpcfg types */ @@ -130,12 +130,14 @@ package pmp_types_1_12_pkg; logic R; } pmpcfg_base_t; -typedef struct packed { - pmpcfg_base_t cfg3; - pmpcfg_base_t cfg2; - pmpcfg_base_t cfg1; - pmpcfg_base_t cfg0; -} pmpcfg_t; +// typedef struct packed { +// pmpcfg_base_t cfg3; +// pmpcfg_base_t cfg2; +// pmpcfg_base_t cfg1; +// pmpcfg_base_t cfg0; +// } pmpcfg_t; + +typedef pmpcfg_base_t [3:0] pmpcfg_t; typedef logic [31:0] pmpaddr_t; diff --git a/source_code/packages/riscv_packages.core b/source_code/packages/riscv_packages.core index a059ee009..d38035e20 100644 --- a/source_code/packages/riscv_packages.core +++ b/source_code/packages/riscv_packages.core @@ -9,6 +9,7 @@ filesets: - rv32i_types_pkg.sv - machine_mode_types_1_12_pkg.sv - pma_types_1_12_pkg.sv + - pmp_types_1_12_pkg.sv - risc_mgmt/crc32_pkg.sv - risc_mgmt/template_pkg.sv - risc_mgmt/test_pkg.sv diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index fb2af663d..97234a885 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -378,9 +378,9 @@ module tspp_execute_stage ( //Send exceptions to Hazard Unit assign hazard_if.illegal_insn = (cu_if.illegal_insn & ~rm_if.ex_token) | prv_pipe_if.invalid_csr; - assign hazard_if.fault_l = 1'b0; + assign hazard_if.fault_l = prv_pipe_if.prot_fault_l; assign hazard_if.mal_l = cu_if.dren & mal_addr; - assign hazard_if.fault_s = 1'b0; + assign hazard_if.fault_s = prv_pipe_if.prot_fault_s; assign hazard_if.mal_s = cu_if.dwen & mal_addr; assign hazard_if.breakpoint = cu_if.breakpoint; assign hazard_if.env_m = cu_if.ecall_insn; diff --git a/source_code/pipelines/tspp/tspp_fetch_stage.sv b/source_code/pipelines/tspp/tspp_fetch_stage.sv index 5dac51c72..5421de016 100644 --- a/source_code/pipelines/tspp/tspp_fetch_stage.sv +++ b/source_code/pipelines/tspp/tspp_fetch_stage.sv @@ -84,7 +84,7 @@ module tspp_fetch_stage ( //Instruction Access logic assign hazard_if.i_mem_busy = igen_bus_if.busy; assign igen_bus_if.addr = rv32cif.rv32c_ena ? rv32cif.imem_pc : pc; - assign igen_bus_if.ren = hazard_if.iren & !rv32cif.done_earlier; + assign igen_bus_if.ren = hazard_if.iren & !rv32cif.done_earlier & ~prv_pipe_if.prot_fault_i; assign igen_bus_if.wen = 1'b0; assign igen_bus_if.byte_en = 4'b1111; assign igen_bus_if.wdata = '0; @@ -108,13 +108,13 @@ module tspp_fetch_stage ( //Send exceptions to Hazard Unit logic mal_addr; assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); - assign hazard_if.fault_insn = 1'b0; + assign hazard_if.fault_insn = prv_pipe_if.prot_fault_i; assign hazard_if.mal_insn = mal_addr; assign hazard_if.badaddr_f = igen_bus_if.addr; assign hazard_if.epc_f = pc; // Send memory protection signals - assign prv_pipe_if.iren = igen_bus_if.ren; + assign prv_pipe_if.iren = 1'b1; assign prv_pipe_if.iaddr = igen_bus_if.addr; assign prv_pipe_if.i_acc_width = WordAcc; diff --git a/source_code/pipelines/tspp/tspp_hazard_unit.sv b/source_code/pipelines/tspp/tspp_hazard_unit.sv index ca27449fc..4d4f89164 100644 --- a/source_code/pipelines/tspp/tspp_hazard_unit.sv +++ b/source_code/pipelines/tspp/tspp_hazard_unit.sv @@ -78,8 +78,8 @@ module tspp_hazard_unit ( assign prv_pipe_if.ret = hazard_if.ret; assign e_ex_stage = hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l | hazard_if.fault_s | hazard_if.mal_s | hazard_if.breakpoint | - hazard_if.env_m | prv_pipe_if.prot_fault_l | prv_pipe_if.prot_fault_s; - assign e_f_stage = hazard_if.fault_insn | hazard_if.mal_insn | prv_pipe_if.prot_fault_i; + hazard_if.env_m; + assign e_f_stage = hazard_if.fault_insn | hazard_if.mal_insn; assign intr = ~e_ex_stage & ~e_f_stage & prv_pipe_if.intr; assign prv_pipe_if.pipe_clear = e_ex_stage | ~(hazard_if.token_ex | rm_if.active_insn); @@ -95,12 +95,12 @@ module tspp_hazard_unit ( assign prv_pipe_if.wb_enable = !hazard_if.if_ex_stall | hazard_if.jump | hazard_if.branch; //Because 2 stages - assign prv_pipe_if.fault_insn = hazard_if.fault_insn | prv_pipe_if.prot_fault_i; + assign prv_pipe_if.fault_insn = hazard_if.fault_insn; assign prv_pipe_if.mal_insn = hazard_if.mal_insn; assign prv_pipe_if.illegal_insn = hazard_if.illegal_insn; - assign prv_pipe_if.fault_l = hazard_if.fault_l | prv_pipe_if.prot_fault_l; + assign prv_pipe_if.fault_l = hazard_if.fault_l; assign prv_pipe_if.mal_l = hazard_if.mal_l; - assign prv_pipe_if.fault_s = hazard_if.fault_s | prv_pipe_if.prot_fault_s; + assign prv_pipe_if.fault_s = hazard_if.fault_s; assign prv_pipe_if.mal_s = hazard_if.mal_s; assign prv_pipe_if.breakpoint = hazard_if.breakpoint; assign prv_pipe_if.env_m = hazard_if.env_m; diff --git a/source_code/privs/priv.core b/source_code/privs/priv.core index f5acbc3ef..ca646fd0a 100644 --- a/source_code/privs/priv.core +++ b/source_code/privs/priv.core @@ -11,6 +11,8 @@ filesets: - priv_1_12/priv_1_12_csr.sv - priv_1_12/priv_1_12_pipe_control.sv - priv_1_12/priv_1_12_pma.sv + - priv_1_12/priv_1_12_pmp.sv + - priv_1_12/priv_1_12_pmp_matcher.sv file_type: systemVerilogSource diff --git a/source_code/privs/priv_1_12/priv_1_12_block.sv b/source_code/privs/priv_1_12/priv_1_12_block.sv index 47b472e3b..55b2423f6 100644 --- a/source_code/privs/priv_1_12/priv_1_12_block.sv +++ b/source_code/privs/priv_1_12/priv_1_12_block.sv @@ -37,11 +37,13 @@ module priv_1_12_block ( priv_1_12_internal_if prv_intern_if(); priv_ext_if priv_ext_pma_if(); + priv_ext_if priv_ext_pmp_if(); - priv_1_12_csr csr (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_pma_if(priv_ext_pma_if)); + priv_1_12_csr csr (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_pma_if(priv_ext_pma_if), .priv_ext_pmp_if(priv_ext_pmp_if)); priv_1_12_int_ex_handler int_ex_handler (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if)); priv_1_12_pipe_control pipe_ctrl (.prv_intern_if(prv_intern_if)); priv_1_12_pma pma (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_if(priv_ext_pma_if)); + priv_1_12_pmp pmp (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_if(priv_ext_pmp_if)); priv_level_t curr_priv; @@ -120,8 +122,8 @@ module priv_1_12_block ( assign prv_intern_if.ren = prv_pipe_if.dren; assign prv_intern_if.wen = prv_pipe_if.dwen; assign prv_intern_if.xen = prv_pipe_if.iren; - assign prv_pipe_if.prot_fault_i = prv_intern_if.pma_i_fault; - assign prv_pipe_if.prot_fault_l = prv_intern_if.pma_l_fault; - assign prv_pipe_if.prot_fault_s = prv_intern_if.pma_s_fault; + assign prv_pipe_if.prot_fault_i = prv_intern_if.pma_i_fault | prv_intern_if.pmp_i_fault; + assign prv_pipe_if.prot_fault_l = prv_intern_if.pma_l_fault | prv_intern_if.pmp_l_fault; + assign prv_pipe_if.prot_fault_s = prv_intern_if.pma_s_fault | prv_intern_if.pmp_s_fault; endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_csr.sv b/source_code/privs/priv_1_12/priv_1_12_csr.sv index 62e081285..2958c0c59 100644 --- a/source_code/privs/priv_1_12/priv_1_12_csr.sv +++ b/source_code/privs/priv_1_12/priv_1_12_csr.sv @@ -32,7 +32,8 @@ module priv_1_12_csr #( input CLK, input nRST, priv_1_12_internal_if.csr prv_intern_if, - priv_ext_if.priv priv_ext_pma_if + priv_ext_if.priv priv_ext_pma_if, + priv_ext_if.priv priv_ext_pmp_if `ifdef RV32F_SUPPORTED , priv_ext_if.priv priv_ext_f_if `endif // RV32F_SUPPORTED @@ -76,14 +77,20 @@ module priv_1_12_csr #( csr_reg_t nxt_csr_val; // invalid_csr flags - logic invalid_csr_0, invalid_csr_1; + logic invalid_csr_0, invalid_csr_1; // 0: lack of privilege, 1: bad address assign prv_intern_if.invalid_csr = invalid_csr_0 | invalid_csr_1; // Extension Broadcast Signals + // - PMA assign priv_ext_pma_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_pma_if.value_in = nxt_csr_val; assign priv_ext_pma_if.csr_active = ~invalid_csr_0 & prv_intern_if.valid_write & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); + // - PMP + assign priv_ext_pmp_if.csr_addr = prv_intern_if.csr_addr; + assign priv_ext_pmp_if.value_in = nxt_csr_val; + assign priv_ext_pmp_if.csr_active = ~invalid_csr_0 & prv_intern_if.valid_write + & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); `ifdef RV32F_SUPPORTED assign priv_ext_f_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_f_if.value_in = nxt_csr_val; @@ -241,7 +248,7 @@ module priv_1_12_csr #( prv_intern_if.new_csr_val; invalid_csr_0 = 1'b0; - if (prv_intern_if.csr_addr[9:8] & prv_intern_if.curr_priv != 2'b11) begin + if (prv_intern_if.csr_addr[9:8] > prv_intern_if.curr_priv) begin if (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear) begin invalid_csr_0 = 1'b1; // Not enough privilege end @@ -249,7 +256,7 @@ module priv_1_12_csr #( if (prv_intern_if.valid_write) begin casez(prv_intern_if.csr_addr) MSTATUS_ADDR: begin - if (prv_intern_if.new_csr_val[12:11] == 2'b10) begin + if (prv_intern_if.new_csr_val[12:11] == RESERVED_MODE) begin mstatus_next.mpp = U_MODE; // If invalid privilege level, dump at 0 end else begin mstatus_next.mpp = priv_level_t'(nxt_csr_val[12:11]); @@ -276,9 +283,9 @@ module priv_1_12_csr #( end MIP_ADDR: begin - mip_next.msip = nxt_csr_val[3]; - mip_next.mtip = nxt_csr_val[7]; - mip_next.meip = nxt_csr_val[11]; + mip_next.msip = nxt_csr_val[3]; + mip_next.mtip = nxt_csr_val[7]; + mip_next.meip = nxt_csr_val[11]; end MSCRATCH_ADDR: begin mscratch_next = nxt_csr_val; @@ -368,6 +375,9 @@ module priv_1_12_csr #( if (priv_ext_pma_if.ack) begin prv_intern_if.old_csr_val = priv_ext_pma_if.value_out; end + if (priv_ext_pmp_if.ack) begin + prv_intern_if.old_csr_val = priv_ext_pmp_if.value_out; + end `ifdef RV32F_SUPPORTED if (priv_ext_f_if.ack) begin prv_intern_if.old_csr_val = priv_ext_f_if.value_out; @@ -382,6 +392,7 @@ module priv_1_12_csr #( // CSR address doesn't exist invalid_csr_1 = 1'b1 & (~priv_ext_pma_if.ack) & (~priv_ext_pma_if.invalid_csr) + & (~priv_ext_pmp_if.ack) & (~priv_ext_pmp_if.invalid_csr) `ifdef RV32F_SUPPORTED & (~priv_ext_f_if.ack) & (~priv_ext_f_if.invalid_csr) `endif // RV32F_SUPPORTED diff --git a/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv index 622fb3bca..cb6bb89ce 100644 --- a/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv +++ b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv @@ -149,12 +149,10 @@ module priv_1_12_int_ex_handler ( else if (prv_intern_if.clear_timer_int_s) prv_intern_if.next_mip.stip = 1'b0; end - assign prv_intern_if.inject_mstatus = exception | prv_intern_if.intr | prv_intern_if.mret - | prv_intern_if.sret | prv_intern_if.uret; + assign prv_intern_if.inject_mstatus = exception | prv_intern_if.intr | prv_intern_if.mret; always_comb begin - prv_intern_if.next_mstatus.mie = prv_intern_if.curr_mstatus.mie; - prv_intern_if.next_mstatus.mpie = prv_intern_if.curr_mstatus.mpie; + prv_intern_if.next_mstatus = prv_intern_if.curr_mstatus; // interrupt has truly been registered and it is time to go to the vector table if (update_mie) begin // when a trap is taken mpie is set to the current mie diff --git a/source_code/privs/priv_1_12/priv_1_12_pma.sv b/source_code/privs/priv_1_12/priv_1_12_pma.sv index dc16e7690..5dbd14a1f 100644 --- a/source_code/privs/priv_1_12/priv_1_12_pma.sv +++ b/source_code/privs/priv_1_12/priv_1_12_pma.sv @@ -39,25 +39,33 @@ module priv_1_12_pma ( pma_cfg_t pma_cfg_d, pma_cfg_i; pma_reg_t new_val; + // Some easy to use config constants + // ROM_PMA - reserved, no W, R, X, WordAcc, Idm, Cache, Coh, RsrvEventual, AMONone, Memory + `define ROM_PMA pma_cfg_t'({2'b0, 1'b0, 1'b1, 1'b1, WordAcc, 1'b1, 1'b1, 1'b1, RsrvEventual, AMONone, 1'b1}) + // RAM_PMA - reserved, W, R, X, WordAcc, Idm, Cache, Coh, RsrvEventual, AMONone, Memory + `define RAM_PMA pma_cfg_t'({2'b0, 1'b1, 1'b1, 1'b1, WordAcc, 1'b1, 1'b1, 1'b1, RsrvEventual, AMONone, 1'b1}) + // IO_PMA - reserved, W, R, X, WordAcc, no Idm, no Cache, Coh, RsrvEventual, AMONone, I/O + `define IO_PMA pma_cfg_t'({2'b0, 1'b1, 1'b1, 1'b1, WordAcc, 1'b0, 1'b0, 1'b1, RsrvEventual, AMONone, 1'b0}) + // Core State Registers always_ff @ (posedge CLK, negedge nRST) begin if (~nRST) begin - pma_regs[00] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h1BF1)}); // 0 - ROM, 1 - RAM - pma_regs[01] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM - pma_regs[02] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM - pma_regs[03] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM - pma_regs[04] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM - pma_regs[05] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM - pma_regs[06] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM - pma_regs[07] <= pma_reg_t'({pma_cfg_t'(16'h3BF1), pma_cfg_t'(16'h3BF1)}); // RAM - pma_regs[08] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O - pma_regs[09] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O - pma_regs[10] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O - pma_regs[11] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O - pma_regs[12] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O - pma_regs[13] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O - pma_regs[14] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O - pma_regs[15] <= pma_reg_t'({pma_cfg_t'(16'h3B30), pma_cfg_t'(16'h3B30)}); // I/O + pma_regs[00] <= pma_reg_t'({`RAM_PMA, `ROM_PMA}); + pma_regs[01] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[02] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[03] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[04] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[05] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[06] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[07] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[08] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[09] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[10] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[11] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[12] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[13] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[14] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[15] <= pma_reg_t'({`IO_PMA, `IO_PMA}); end else begin pma_regs <= nxt_pma_regs; end @@ -79,10 +87,10 @@ module priv_1_12_pma ( new_val.pma_cfg_0.Rsrv = RsrvNone; end if (new_val.pma_cfg_0.AccWidth == AccWidthReserved) begin - new_val.pma_cfg_0.Rsrv = WordAcc; + new_val.pma_cfg_0.AccWidth = WordAcc; end if (new_val.pma_cfg_1.AccWidth == AccWidthReserved) begin - new_val.pma_cfg_0.Rsrv = WordAcc; + new_val.pma_cfg_0.AccWidth = WordAcc; end nxt_pma_regs[priv_ext_if.csr_addr[3:0]] = new_val; diff --git a/source_code/privs/priv_1_12/priv_1_12_pmp.sv b/source_code/privs/priv_1_12/priv_1_12_pmp.sv new file mode 100644 index 000000000..77744cd88 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pmp.sv @@ -0,0 +1,272 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_pmp.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 10/31/2022 +* Description: PMP Unit, version 1.12 +*/ + +`include "priv_1_12_internal_if.vh" +`include "priv_ext_if.vh" + +module priv_1_12_pmp ( + input logic CLK, nRST, + priv_1_12_internal_if.pmp prv_intern_if, + priv_ext_if.ext priv_ext_if +); + + import pmp_types_1_12_pkg::*; + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + pmpcfg_t [3:0] pmp_cfg_regs, nxt_pmp_cfg; + pmpaddr_t [15:0] pmp_addr_regs, nxt_pmp_addr; + pmpcfg_t new_cfg; + + // Core State Registers + always_ff @ (posedge CLK, negedge nRST) begin + if (~nRST) begin + pmp_cfg_regs <= '0; + pmp_addr_regs <= '0; + end else begin + pmp_cfg_regs <= nxt_pmp_cfg; + pmp_addr_regs <= nxt_pmp_addr; + end + end + + // Core State Logic + logic [1:0] pmp_cfg_addr_add_one_reg = (priv_ext_if.csr_addr[3:0] + 1) >> 2; // exists because TOR is weird + logic [1:0] pmp_cfg_addr_add_one_cfg = (priv_ext_if.csr_addr[3:0] + 1) & 2'h3; // exists because TOR is weird + always_comb begin + nxt_pmp_addr = pmp_addr_regs; + nxt_pmp_cfg = pmp_cfg_regs; + new_cfg = pmpcfg_t'(priv_ext_if.value_in); + if (priv_ext_if.csr_active) begin + casez(priv_ext_if.csr_addr) + 12'b0011_1010_00??: begin // 0x3A0 + // WARL check (reserved) + new_cfg[0].reserved = '0; + new_cfg[1].reserved = '0; + new_cfg[2].reserved = '0; + new_cfg[3].reserved = '0; + // WARL check (R/W) + if (new_cfg[0].R == 1'b0 && new_cfg[0].W == 1'b1) begin + new_cfg[0].W = 1'b0; + end + if (new_cfg[1].R == 1'b0 && new_cfg[1].W == 1'b1) begin + new_cfg[1].W = 1'b0; + end + if (new_cfg[2].R == 1'b0 && new_cfg[2].W == 1'b1) begin + new_cfg[2].W = 1'b0; + end + if (new_cfg[3].R == 1'b0 && new_cfg[3].W == 1'b1) begin + new_cfg[3].W = 1'b0; + end + + // Make sure we cannot write to locked CSRs + if (pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][0].L) begin + new_cfg[0] = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][0]; + end + if (pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][1].L) begin + new_cfg[1] = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][1]; + end + if (pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][2].L) begin + new_cfg[2] = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][2]; + end + if (pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][3].L) begin + new_cfg[3] = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][3]; + end + + // Assign field + nxt_pmp_cfg[priv_ext_if.csr_addr[1:0]] = new_cfg; + end + 12'b0011_1011_????: begin // 0x3B0 + // Make sure we cannot write to locked CSRs + if (~pmp_cfg_regs[priv_ext_if.csr_addr[3:2]][priv_ext_if.csr_addr[1:0]].L) begin + // But wait, TOR messes things up - we need to check the cfg above it + // pmpcfg(i) might be TOR, which means it uses both pmpaddr(i) and pmpaddr(i-1) + // pg 60 of the v1.12 specification for more info + if (priv_ext_if.csr_addr[3:0] != 15) begin // 15 is the last valid register, can't check the one above it + if (pmp_cfg_regs[pmp_cfg_addr_add_one_reg][pmp_cfg_addr_add_one_cfg].A != TOR) begin // If not TOR, everything is good + nxt_pmp_addr[priv_ext_if.csr_addr[3:0]] = priv_ext_if.value_in; + end else if (~pmp_cfg_regs[pmp_cfg_addr_add_one_reg][pmp_cfg_addr_add_one_cfg].L) begin // It was TOR, and is not locked + nxt_pmp_addr[priv_ext_if.csr_addr[3:0]] = priv_ext_if.value_in; + end + end + end + end + endcase + end + end + + assign priv_ext_if.invalid_csr = 1'b0; + + // Return the right value back + always_comb begin + priv_ext_if.value_out = '0; + priv_ext_if.ack = 1'b1; + casez(priv_ext_if.csr_addr) + 12'b0011_1010_00??: begin + priv_ext_if.value_out = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]]; + end + 12'b0011_1011_????: begin + priv_ext_if.value_out = pmp_addr_regs[priv_ext_if.csr_addr[3:0]]; + end + default: begin + priv_ext_if.ack = 1'b0; + end + endcase + end + + /***** Data PMP checker unit *****/ + logic [15:0] d_cfg_match; + genvar i; + + generate + for (i=0; i<16; i++) begin + priv_1_12_pmp_matcher matcher ( + {2'b00, prv_intern_if.daddr[31:2]}, + pmp_cfg_regs[i>>2][i & 3], + pmp_addr_regs[i], + i == 0 ? '0 : pmp_addr_regs[i-1], + d_cfg_match[i] + ); + end + endgenerate + + // time to use the matches + pmpcfg_base_t d_match; + logic d_match_found; + logic d_prot_fault; + always_comb begin + d_match = '0; + d_match_found = 1'b1; + d_prot_fault = 1'b0; + casez(d_cfg_match) + 16'b????_????_????_???1: d_match = pmp_cfg_regs[0][0]; + 16'b????_????_????_??10: d_match = pmp_cfg_regs[0][1]; + 16'b????_????_????_?100: d_match = pmp_cfg_regs[0][2]; + 16'b????_????_????_1000: d_match = pmp_cfg_regs[0][3]; + 16'b????_????_???1_0000: d_match = pmp_cfg_regs[1][0]; + 16'b????_????_??10_0000: d_match = pmp_cfg_regs[1][1]; + 16'b????_????_?100_0000: d_match = pmp_cfg_regs[1][2]; + 16'b????_????_1000_0000: d_match = pmp_cfg_regs[1][3]; + 16'b????_???1_0000_0000: d_match = pmp_cfg_regs[2][0]; + 16'b????_??10_0000_0000: d_match = pmp_cfg_regs[2][1]; + 16'b????_?100_0000_0000: d_match = pmp_cfg_regs[2][2]; + 16'b????_1000_0000_0000: d_match = pmp_cfg_regs[2][3]; + 16'b???1_0000_0000_0000: d_match = pmp_cfg_regs[3][0]; + 16'b??10_0000_0000_0000: d_match = pmp_cfg_regs[3][1]; + 16'b?100_0000_0000_0000: d_match = pmp_cfg_regs[3][2]; + 16'b1000_0000_0000_0000: d_match = pmp_cfg_regs[3][3]; + default: d_match_found = 1'b0; + endcase + + if (prv_intern_if.curr_priv != M_MODE || (prv_intern_if.curr_mstatus.mprv && prv_intern_if.curr_mstatus.mpp != M_MODE)) begin // Core is in an unprivileged state or needs privilege checks + if (~d_match_found) begin + d_prot_fault = 1'b1; + end else begin + if ((prv_intern_if.ren & ~d_match.R) || (prv_intern_if.wen & ~d_match.W)) begin + d_prot_fault = 1'b1; + end + end + end else begin // Core is in M_MODE with no privilege check requirements + if (d_match_found & d_match.L) begin + if ((prv_intern_if.ren & ~d_match.R) || (prv_intern_if.wen & ~d_match.W)) begin + d_prot_fault = 1'b1; + end + end + end + end + + /***** Instruction PMP checker unit *****/ + logic [15:0] i_cfg_match; + genvar j; + + generate + for (j=0; j<16; j++) begin + priv_1_12_pmp_matcher matcher ( + {2'b00, prv_intern_if.iaddr[31:2]}, + pmp_cfg_regs[j>>2][j%4], + pmp_addr_regs[j], + j == 0 ? '0 : pmp_addr_regs[j-1], + i_cfg_match[j] + ); + end + endgenerate + + // time to use the matches + pmpcfg_base_t i_match; + logic i_match_found; + logic i_prot_fault; + always_comb begin + i_match = '0; + i_match_found = 1'b1; + i_prot_fault = 1'b0; + casez(i_cfg_match) + 16'b????_????_????_???1: i_match = pmp_cfg_regs[0][0]; + 16'b????_????_????_??10: i_match = pmp_cfg_regs[0][1]; + 16'b????_????_????_?100: i_match = pmp_cfg_regs[0][2]; + 16'b????_????_????_1000: i_match = pmp_cfg_regs[0][3]; + 16'b????_????_???1_0000: i_match = pmp_cfg_regs[1][0]; + 16'b????_????_??10_0000: i_match = pmp_cfg_regs[1][1]; + 16'b????_????_?100_0000: i_match = pmp_cfg_regs[1][2]; + 16'b????_????_1000_0000: i_match = pmp_cfg_regs[1][3]; + 16'b????_???1_0000_0000: i_match = pmp_cfg_regs[2][0]; + 16'b????_??10_0000_0000: i_match = pmp_cfg_regs[2][1]; + 16'b????_?100_0000_0000: i_match = pmp_cfg_regs[2][2]; + 16'b????_1000_0000_0000: i_match = pmp_cfg_regs[2][3]; + 16'b???1_0000_0000_0000: i_match = pmp_cfg_regs[3][0]; + 16'b??10_0000_0000_0000: i_match = pmp_cfg_regs[3][1]; + 16'b?100_0000_0000_0000: i_match = pmp_cfg_regs[3][2]; + 16'b1000_0000_0000_0000: i_match = pmp_cfg_regs[3][3]; + default: i_match_found = 1'b0; + endcase + + if (prv_intern_if.curr_priv != M_MODE || (prv_intern_if.curr_mstatus.mprv && prv_intern_if.curr_mstatus.mpp != M_MODE)) begin // Core is in an unprivileged state or needs privilege checks + if (~i_match_found) begin + i_prot_fault = 1'b1; + end else begin + if ((prv_intern_if.ren & ~i_match.R) || (prv_intern_if.wen & ~i_match.W)) begin + i_prot_fault = 1'b1; + end + end + end else begin // Core is in M_MODE with no privilege check requirements + if (d_match_found & i_match.L) begin + if ((prv_intern_if.ren & ~i_match.R) || (prv_intern_if.wen & ~i_match.W)) begin + i_prot_fault = 1'b1; + end + end + end + end + + /***** Resolve and output D and I faults *****/ + always_comb begin + prv_intern_if.pmp_s_fault = 1'b0; + prv_intern_if.pmp_l_fault = 1'b0; + prv_intern_if.pmp_i_fault = 1'b0; + if (d_prot_fault) begin + prv_intern_if.pmp_s_fault = prv_intern_if.wen; + prv_intern_if.pmp_l_fault = prv_intern_if.ren; + end else if (i_prot_fault) begin + prv_intern_if.pmp_i_fault = prv_intern_if.xen; + end + end + +endmodule \ No newline at end of file diff --git a/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.py b/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.py new file mode 100644 index 000000000..70d5a1793 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.py @@ -0,0 +1,14 @@ +# Generates the NAPOT encoder +# really only works for 32-bit addresses + +XLEN = 32 + +print("casez(cfg_addr)") + +for i in range(1, XLEN+1): + print(" 32'b", end="") + print(("?" * (XLEN - i)) + ("0" if i != (XLEN+1) else "") + ("1"*(i-1)) + ": ", end="") + print(f"match = phys_addr[31:{i:02}] == cfg_addr[31:{i:02}];" if i < XLEN-1 else ("match = phys_addr[31] == cfg_addr[31];" if i == XLEN-1 else "match = 1'b1;")) + +print(" default: match = 1'b0;") +print("endcase") \ No newline at end of file diff --git a/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.sv b/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.sv new file mode 100644 index 000000000..b342fc7e6 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.sv @@ -0,0 +1,98 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_pmp_matcher.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 11/08/2022 +* Description: Internal module used to indicate a PMP + configuration match. +*/ + +import pmp_types_1_12_pkg::*; + +module priv_1_12_pmp_matcher( + input logic[31:0] phys_addr, // address to check + input pmpcfg_base_t check_cfg, // configuration to match + input pmpaddr_t cfg_addr, // configuration address + input pmpaddr_t cfg_addr_before, // configuration address of previous cfg_addr, used for TOR + output logic match // did the config match? +); + + always_comb begin + match = 1'b0; + casez(check_cfg.A) + NA4: begin + match = phys_addr == cfg_addr; + end + + TOR: begin + if (cfg_addr_before >= cfg_addr) begin + match = 1'b0; // If the previous address is greater/equal, no match + end else begin + match = (phys_addr >= cfg_addr_before) && (phys_addr < cfg_addr); + end + end + + NAPOT: begin + // I highly suggest using the script in this folder to create the table below. + // the all 1's case doesn't make sense to me, so I didn't include it here. + casez(cfg_addr) + 32'b???????????????????????????????0: match = phys_addr[31:01] == cfg_addr[31:01]; + 32'b??????????????????????????????01: match = phys_addr[31:02] == cfg_addr[31:02]; + 32'b?????????????????????????????011: match = phys_addr[31:03] == cfg_addr[31:03]; + 32'b????????????????????????????0111: match = phys_addr[31:04] == cfg_addr[31:04]; + 32'b???????????????????????????01111: match = phys_addr[31:05] == cfg_addr[31:05]; + 32'b??????????????????????????011111: match = phys_addr[31:06] == cfg_addr[31:06]; + 32'b?????????????????????????0111111: match = phys_addr[31:07] == cfg_addr[31:07]; + 32'b????????????????????????01111111: match = phys_addr[31:08] == cfg_addr[31:08]; + 32'b???????????????????????011111111: match = phys_addr[31:09] == cfg_addr[31:09]; + 32'b??????????????????????0111111111: match = phys_addr[31:10] == cfg_addr[31:10]; + 32'b?????????????????????01111111111: match = phys_addr[31:11] == cfg_addr[31:11]; + 32'b????????????????????011111111111: match = phys_addr[31:12] == cfg_addr[31:12]; + 32'b???????????????????0111111111111: match = phys_addr[31:13] == cfg_addr[31:13]; + 32'b??????????????????01111111111111: match = phys_addr[31:14] == cfg_addr[31:14]; + 32'b?????????????????011111111111111: match = phys_addr[31:15] == cfg_addr[31:15]; + 32'b????????????????0111111111111111: match = phys_addr[31:16] == cfg_addr[31:16]; + 32'b???????????????01111111111111111: match = phys_addr[31:17] == cfg_addr[31:17]; + 32'b??????????????011111111111111111: match = phys_addr[31:18] == cfg_addr[31:18]; + 32'b?????????????0111111111111111111: match = phys_addr[31:19] == cfg_addr[31:19]; + 32'b????????????01111111111111111111: match = phys_addr[31:20] == cfg_addr[31:20]; + 32'b???????????011111111111111111111: match = phys_addr[31:21] == cfg_addr[31:21]; + 32'b??????????0111111111111111111111: match = phys_addr[31:22] == cfg_addr[31:22]; + 32'b?????????01111111111111111111111: match = phys_addr[31:23] == cfg_addr[31:23]; + 32'b????????011111111111111111111111: match = phys_addr[31:24] == cfg_addr[31:24]; + 32'b???????0111111111111111111111111: match = phys_addr[31:25] == cfg_addr[31:25]; + 32'b??????01111111111111111111111111: match = phys_addr[31:26] == cfg_addr[31:26]; + 32'b?????011111111111111111111111111: match = phys_addr[31:27] == cfg_addr[31:27]; + 32'b????0111111111111111111111111111: match = phys_addr[31:28] == cfg_addr[31:28]; + 32'b???01111111111111111111111111111: match = phys_addr[31:29] == cfg_addr[31:29]; + 32'b??011111111111111111111111111111: match = phys_addr[31:30] == cfg_addr[31:30]; + 32'b?0111111111111111111111111111111: match = phys_addr[31] == cfg_addr[31]; + 32'b01111111111111111111111111111111, + 32'b11111111111111111111111111111111: match = 1'b1; + default: match = 1'b0; + endcase + end + + OFF: begin + match = 1'b0; + end + endcase + end + +endmodule \ No newline at end of file diff --git a/verification/pmp-tests/build_all.py b/verification/pmp-tests/build_all.py new file mode 100755 index 000000000..f306e6c5e --- /dev/null +++ b/verification/pmp-tests/build_all.py @@ -0,0 +1,51 @@ +#! /usr/bin/python3 + +import subprocess +import glob +import os +import pathlib + + +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i', '-mabi=ilp32', '-mcmodel=medany', + '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] + +cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] + + +if not os.path.isfile('./start.S') or not os.path.isfile('link.ld'): + print('Error: Could not find AFTx06.S or link.ld in this directory') + exit(1) + + +for fname in (glob.glob('./*.c') + glob.glob('./*.S')): + if 'start' in fname or 'utility' in fname: + print("Skipping {} as top-level file, appears to be a utility".format(fname)); + continue + print('Compiling {}'.format(fname)) + basename = pathlib.Path(fname).stem + + rv = subprocess.run(compile_cmd + [fname, '-o', basename + '.elf'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + print('Converting {} to binary'.format(fname)) + rv = subprocess.run(cvt_cmd + [basename + '.elf', basename + '.bin']) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + +print( +''' + Finished compilation. Now, pass the '.bin' file corresponding to + the example to run as an argument to 'VbASIC_wrapper' to run an example! +''') diff --git a/verification/pmp-tests/link.ld b/verification/pmp-tests/link.ld new file mode 100755 index 000000000..4fd20ea69 --- /dev/null +++ b/verification/pmp-tests/link.ld @@ -0,0 +1,14 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x0000000080000000; + .text.init : { *(.text.init) } + .text : { *(.text) } + .data ALIGN(0x1000) : { *(.data) } + .bss : { *(.bss) } + . = 0x0000000080007000; + .statuses : { *(.statuses) } + _end = .; +} diff --git a/verification/pmp-tests/pmp_csr.c b/verification/pmp-tests/pmp_csr.c new file mode 100644 index 000000000..504575e51 --- /dev/null +++ b/verification/pmp-tests/pmp_csr.c @@ -0,0 +1,137 @@ +#include +#include "utility.h" + +extern volatile int flag; + +int main() { + flag = 9; + + // 0.0 Try to write a valid value to the pmpaddr0 register + uint32_t pmp_addr = 0xDEADBEEF; + asm volatile("csrw pmpaddr0, %0" : : "r"(pmp_addr)); + // 0.0 Read the value back + pmp_addr = 0x0; + asm volatile("csrr %0, pmpaddr0" : "=r"(pmp_addr)); + if (pmp_addr == 0xDEADBEEF) + { + flag -= 1; + } + else + { + print("Case 0 wrong value\n"); + } + + // 1.0 Try to write a valid configuration to the pmpcfg0 register + uint32_t pmp_cfg = 0x00170017; + asm volatile("csrw pmpcfg0, %0" : : "r"(pmp_cfg)); + // 1.1 Read the value back + pmp_cfg = 0x0; + asm volatile("csrr %0, pmpcfg0" : "=r"(pmp_cfg)); + if (pmp_cfg == 0x00170017) + { + flag -= 1; + } + else + { + print("Case 1 wrong value\n"); + put_uint32_hex(pmp_cfg); + } + + // 2.0 Try to write an invalid configuration to the pmpcfg1 register + pmp_cfg = 0x001A0027; // pmp4cfg has a non-0 reserved field, pmp6cfg has an invalid permission + asm volatile("csrw pmpcfg1, %0" : : "r"(pmp_cfg)); + // 2.1 Read the value back + pmp_cfg = 0x0; + asm volatile("csrr %0, pmpcfg1" : "=r"(pmp_cfg)); + if (pmp_cfg == 0x00180007) + { + flag -= 1; + } + else + { + print("Case 2 wrong value\n"); + put_uint32_hex(pmp_cfg); + } + + // 3.0 Lock and try to write a configuration to the pmpcfg2 register + pmp_cfg = 0x00000088; + asm volatile("csrw pmpcfg2, %0" : : "r"(pmp_cfg)); + pmp_cfg = 0x00000007; + asm volatile("csrs pmpcfg2, %0" : : "r"(pmp_cfg)); + // 3.1 Try to read the value back + pmp_cfg = 0x0; + asm volatile("csrr %0, pmpcfg2" : "=r"(pmp_cfg)); + if (pmp_cfg == 0x00000088) + { + flag -= 1; + } + else + { + print("Case 3.0 wrong value\n"); + put_uint32_hex(pmp_cfg); + } + // 3.2 Try to write a neighbor config + pmp_cfg = 0x00070000; + asm volatile("csrs pmpcfg2, %0" : : "r"(pmp_cfg)); + // 3.3 Read and verify the second one wrote + pmp_cfg = 0x0; + asm volatile("csrr %0, pmpcfg2" : "=r"(pmp_cfg)); + if (pmp_cfg = 0x00070088) + { + flag -= 1; + } + else + { + print("Case 3.2 wrong value\n"); + put_uint32_hex(pmp_cfg); + } + + // 4.0 Try to write a value to the pmpaddr8 register + pmp_addr = 0xDEADBEEF; + asm volatile("csrw pmpaddr8, %0" : : "r"(pmp_addr)); + // 4.1 Try to read it back + pmp_addr = 0x0; + asm volatile("csrr %0, pmpaddr8" : "=r"(pmp_addr)); + if (pmp_addr == 0x00000000) + { + flag -= 1; + } + else + { + print("Case 4.0 wrong value\n"); + put_uint32_hex(pmp_addr); + } + // 4.2 Try to write a value to the pmpaddr7 register + // this should fail because pmp8cfg is TOR + pmp_addr = 0xDEADBEEF; + asm volatile("csrw pmpaddr7, %0" : : "r"(pmp_addr)); + // 4.3 Try to read it back + pmp_addr = 0x0; + asm volatile("csrr %0, pmpaddr7" : "=r"(pmp_addr)); + if (pmp_addr == 0x00000000) + { + flag -= 1; + } + else + { + print("Case 4.2 wrong value\n"); + put_uint32_hex(pmp_addr); + } + // 4.4 Write to a different address + pmp_addr = 0xDEADBEEF; + asm volatile("csrw pmpaddr9, %0" : : "r"(pmp_addr)); + // 4.5 Read and verify + pmp_addr = 0x0; + asm volatile("csrr %0, pmpaddr9" : "=r"(pmp_addr)); + if (pmp_addr == 0xDEADBEEF) + { + flag -= 1; + } + else + { + print("Case 4.4 wrong value\n"); + put_uint32_hex(pmp_addr); + } + + return 0; +} diff --git a/verification/pmp-tests/pmp_na4.c b/verification/pmp-tests/pmp_na4.c new file mode 100644 index 000000000..3bde3cfa9 --- /dev/null +++ b/verification/pmp-tests/pmp_na4.c @@ -0,0 +1,54 @@ +#include +#include "utility.h" + +extern volatile int flag; + +#define BAD_PMP_ADDR 0x40000000 // This is a 32-bit address +volatile uint32_t *bad_pmp_addr = (uint32_t*) BAD_PMP_ADDR; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + print("PMP Unit Handler tripped\n"); + flag -= 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + flag = 4; + + // 0. Setup the instruction/stack/MMIO regions + uint32_t pmp_cfg = 0x001F1F00; + asm volatile("csrw pmpcfg0, %0" : : "r" (pmp_cfg)); + uint32_t pmp_addr = (0x80000000 >> 2) & ~((1 << 14) - 1) | ((1 << (14 - 1)) - 1); + asm volatile("csrw pmpaddr1, %0" : : "r" (pmp_addr)); + pmp_addr = (0xFFFFFFE0 >> 2) & ~((1 << 4) - 1) | ((1 << (4 - 1)) - 1); + asm volatile("csrw pmpaddr2, %0" : : "r" (pmp_addr)); + + // 1. Test PMP, NA4 in M Mode + pmp_cfg = 0x00000010; // set pmpcfg0.pmp0cfg to (no L, NA4, no RWX) + pmp_addr = BAD_PMP_ADDR >> 2; // set pmpaddr0 to the bad address, chop off bottom 2 bits + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + asm volatile("csrw pmpaddr0, %0" : : "r" (pmp_addr)); + *bad_pmp_addr = 0xDEADBEEF; // should succeed + flag -= 1; + + // 2. Test PMP, NA4 with MPRV + uint32_t mstatus = 0x20000; // set mstatus.mprv, mpp should be 2'b00 + asm volatile("csrw mstatus, %0" : : "r" (mstatus)); + *bad_pmp_addr = 0xABCD1234; // should fail + + // 3. Test PMP, NA4 with L register + asm volatile("csrc mstatus, %0" : : "r" (mstatus)); // clear mstatus.mprv + pmp_cfg = 0x00000090; // set pmpcfg0.pmp0cfg to (L, NA4, no RWX) + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + *bad_pmp_addr = 0x0987FEDC; // should fail + + return 0; +} diff --git a/verification/pmp-tests/pmp_napot.c b/verification/pmp-tests/pmp_napot.c new file mode 100644 index 000000000..17a94c5d0 --- /dev/null +++ b/verification/pmp-tests/pmp_napot.c @@ -0,0 +1,63 @@ +#include +#include "utility.h" + +extern volatile int flag; + +#define BAD_PMP_ADDR 0x40000000 // This is a 32-bit address +#define G 4 +volatile uint32_t *bad_pmp_addr = (uint32_t*) BAD_PMP_ADDR; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + uint32_t mtval; + asm volatile("csrr %0, mtval" : "=r"(mtval)); + + print("PMP Unit Handler tripped: "); + put_uint32_hex(mtval >>2); + flag -= 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + flag = 7; + + // 0. Setup the instruction/stack/MMIO regions + uint32_t pmp_cfg = 0x001F1F00; + asm volatile("csrw pmpcfg0, %0" : : "r" (pmp_cfg)); + uint32_t pmp_addr = (0x80000000 >> 2) & ~((1 << 14) - 1) | ((1 << (14 - 1)) - 1); + asm volatile("csrw pmpaddr1, %0" : : "r" (pmp_addr)); + pmp_addr = (0xFFFFFFE0 >> 2) & ~((1 << 4) - 1) | ((1 << (4 - 1)) - 1); + asm volatile("csrw pmpaddr2, %0" : : "r" (pmp_addr)); + + // 1. Test PMP, NAPOT in M Mode + pmp_cfg = 0x00000018; // set pmpcfg0.pmp0cfg to (no L, NAPOT, no RWX) + pmp_addr = ((BAD_PMP_ADDR >> 2) & ~((1 << G) - 1)) | ((1 << (G - 1)) - 1); // set pmpaddr0 to the bad address, chop off bottom 2 bits and add in the NAPOT mask + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + asm volatile("csrw pmpaddr0, %0" : : "r" (pmp_addr)); + *bad_pmp_addr = 0xDEADBEEF; // should succeed + flag -= 1; + *(bad_pmp_addr + 4) = 0xDEADBEEF; //should succeed + flag -= 1; + + // 2. Test PMP, NAPOT with MPRV + uint32_t mstatus = 0x20000; // set mstatus.mprv, mpp should be 2'b00 + asm volatile("csrw mstatus, %0" : : "r" (mstatus)); + *bad_pmp_addr = 0xABCD1234; // should fail + *(bad_pmp_addr + 4) = 0xABCD1234; //should fail + + // 3. Test PMP, NAPOT with L register + asm volatile("csrc mstatus, %0" : : "r" (mstatus)); // clear mstatus.mprv + pmp_cfg = 0x00000098; // set pmpcfg0.pmp0cfg to (L, NAPOT, no RWX) + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + *bad_pmp_addr = 0x0987FEDC; // should fail + *(bad_pmp_addr + 4) = 0x0987FEDC; //should fail + + return 0; +} diff --git a/verification/pmp-tests/pmp_tor.c b/verification/pmp-tests/pmp_tor.c new file mode 100644 index 000000000..d454ca048 --- /dev/null +++ b/verification/pmp-tests/pmp_tor.c @@ -0,0 +1,67 @@ +#include +#include "utility.h" + +extern volatile int flag; + +#define BAD_PMP_BOT 0x40000000 // This is a 32-bit address +#define BAD_PMP_TOP 0x40000020 // This is a 32-bit address +volatile uint32_t *bad_pmp_addr_top = (uint32_t*) BAD_PMP_TOP; +volatile uint32_t *bad_pmp_addr_bot = (uint32_t*) BAD_PMP_BOT; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + uint32_t mtval; + asm volatile("csrr %0, mtval" : "=r"(mtval)); + + print("PMP Unit Handler tripped: "); + put_uint32_hex(mtval >> 2); + flag -= 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + flag = 7; + + // 0. Setup the instruction/stack/MMIO regions + uint32_t pmp_cfg = 0x1F1F0000; + asm volatile("csrw pmpcfg0, %0" : : "r" (pmp_cfg)); + uint32_t pmp_addr = (0x80000000 >> 2) & ~((1 << 14) - 1) | ((1 << (14 - 1)) - 1); + asm volatile("csrw pmpaddr2, %0" : : "r" (pmp_addr)); + pmp_addr = (0xFFFFFFE0 >> 2) & ~((1 << 4) - 1) | ((1 << (4 - 1)) - 1); + asm volatile("csrw pmpaddr3, %0" : : "r" (pmp_addr)); + + // 1. Test PMP, TOR in M Mode + pmp_cfg = 0x00000800; // set pmpcfg0.pmp1cfg to (no L, TOR, no RWX) + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + pmp_addr = (BAD_PMP_TOP >> 2); // set pmpaddr1 to the top of range address + asm volatile("csrw pmpaddr1, %0" : : "r" (pmp_addr)); + pmp_addr = (BAD_PMP_BOT >> 2); // set pmpaddr0 to the bottom of range address + asm volatile("csrw pmpaddr0, %0" : : "r" (pmp_addr)); + *(bad_pmp_addr_bot + 4) = 0xDEADBEEF; // should succeed + flag -= 1; + *(bad_pmp_addr_top) = 0xDEADBEEF; // should succeed + flag -= 1; + + // 2. Test PMP, NAPOT with MPRV + uint32_t mstatus = 0x20000; // set mstatus.mprv, mpp should be 2'b00 + asm volatile("csrw mstatus, %0" : : "r" (mstatus)); + *(bad_pmp_addr_bot + 4) = 0xABCD1234; // should fail + *(bad_pmp_addr_top) = 0xABCD1234; // should fail + + // 3. Test PMP, NAPOT with L register + asm volatile("csrc mstatus, %0" : : "r" (mstatus)); // clear mstatus.mprv + pmp_cfg = 0x00008000; // set pmpcfg0.pmp0cfg to (L, TOR, no RWX) + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + *(bad_pmp_addr_bot + 4) = 0x0987FEDC; // should fail + *(bad_pmp_addr_top) = 0x0987FEDC; // should succeed + flag -= 1; + + return 0; +} diff --git a/verification/pmp-tests/start.S b/verification/pmp-tests/start.S new file mode 100644 index 000000000..9e588dd07 --- /dev/null +++ b/verification/pmp-tests/start.S @@ -0,0 +1,17 @@ + +.global _start +_start: + li sp, 0x8000F000 + jal main + +.global done +done: + la x28, flag + lw x28, 0(x28) +__inf_loop: + j __inf_loop + + +.global flag +.data +flag: .word 0xFF diff --git a/verification/pmp-tests/utility.c b/verification/pmp-tests/utility.c new file mode 100644 index 000000000..b346c8e39 --- /dev/null +++ b/verification/pmp-tests/utility.c @@ -0,0 +1,26 @@ + +#include "utility.h" + +void print(char *string) { + volatile char *magic = (volatile char *)MAGIC_ADDR; + + for(int i = 0; string[i]; i++) { + (*magic) = string[i]; + } +} + +void put_uint32_hex(uint32_t x) { + char buf[10] = {0}; + + for(int i = 0; i < 8; i++) { + uint8_t value = (x & 0xF); + if(value >= 10) { + buf[7-i] = ((value-10) + 'A'); + } else { + buf[7-i] = (value + '0'); + } + x >>= 4; + } + buf[8] = '\n'; + print(buf); +} diff --git a/verification/pmp-tests/utility.h b/verification/pmp-tests/utility.h new file mode 100644 index 000000000..6caaee7c5 --- /dev/null +++ b/verification/pmp-tests/utility.h @@ -0,0 +1,19 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + + #include + + #define MTIME_ADDR 0xFFFFFFE0 + #define MTIMEH_ADDR 0xFFFFFFE4 + #define MTIMECMP_ADDR 0xFFFFFFE8 + #define MTIMECMPH_ADDR 0xFFFFFFEC + #define MSIP_ADDR 0xFFFFFFF0 + #define EXT_ADDR_SET 0xFFFFFFF4 + #define EXT_ADDR_CLEAR 0xFFFFFFF8 + #define MAGIC_ADDR 0xFFFFFFFC + + + void print(char *string); + void put_uint32_hex(uint32_t hex); + +#endif From 86825163307445ed6292a098ad0130727d796079 Mon Sep 17 00:00:00 2001 From: Hadi Ahmed Date: Wed, 30 Nov 2022 11:59:53 -0500 Subject: [PATCH 18/47] User Mode, v1.12 implementation (#23) Implementation of User Mode, Priv 1.12 --- Makefile | 5 + source_code/include/priv_1_12_internal_if.vh | 27 ++-- source_code/include/prv_pipeline_if.vh | 16 +- source_code/include/tspp_hazard_unit_if.vh | 6 +- .../packages/machine_mode_types_1_12_pkg.sv | 68 +++++++++ .../pipelines/tspp/tspp_execute_stage.sv | 6 +- .../pipelines/tspp/tspp_hazard_unit.sv | 4 +- source_code/privs/priv.core | 1 + .../privs/priv_1_12/priv_1_12_block.sv | 19 ++- source_code/privs/priv_1_12/priv_1_12_csr.sv | 143 ++++++++++++++---- .../priv_1_12/priv_1_12_int_ex_handler.sv | 77 +++------- source_code/privs/priv_1_12/priv_1_12_mode.sv | 59 ++++++++ .../privs/priv_1_12/priv_1_12_pipe_control.sv | 7 +- source_code/privs/priv_1_12/priv_1_12_pmp.sv | 8 +- source_code/privs/priv_wrapper.sv | 4 +- source_code/standard_core/RISCVBusiness.sv | 7 +- source_code/standard_core/top_core.sv | 1 + tb_core.cc | 4 + .../interrupts-exceptions/ex_after_int.c | 127 ++++++++++++++++ verification/interrupts-exceptions/utility.c | 4 +- verification/interrupts-exceptions/vectored.c | 1 + verification/u-mode/build_all.py | 51 +++++++ verification/u-mode/csr.c | 96 ++++++++++++ verification/u-mode/link.ld | 14 ++ verification/u-mode/start.S | 17 +++ verification/u-mode/u-mode.c | 86 +++++++++++ verification/u-mode/utility.c | 25 +++ verification/u-mode/utility.h | 24 +++ 28 files changed, 769 insertions(+), 138 deletions(-) create mode 100644 source_code/privs/priv_1_12/priv_1_12_mode.sv create mode 100644 verification/interrupts-exceptions/ex_after_int.c create mode 100755 verification/u-mode/build_all.py create mode 100644 verification/u-mode/csr.c create mode 100755 verification/u-mode/link.ld create mode 100644 verification/u-mode/start.S create mode 100644 verification/u-mode/u-mode.c create mode 100644 verification/u-mode/utility.c create mode 100644 verification/u-mode/utility.h diff --git a/Makefile b/Makefile index c89ea5808..25b883953 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,7 @@ define USAGE @echo " config: config core with example.yml" @echo " verilate: Invoke 'FuseSoC run --build' to build Verilator target" @echo " xcelium: Invoke 'FuseSoC run --build' to build Xcelium target" +@echo " lint: Invoke 'FuseSoC run --build' to run the Verilator lint target" @echo " clean: Remove build directories" @echo " veryclean: Remove fusesoc libraries & build directories" @echo "----------------------------------------------------------------------" @@ -67,6 +68,10 @@ xcelium: config @echo "Build finished, you can run with 'fusesoc run', or by navigating" @echo "to the build directory created by FuseSoC and using the Makefile there." +lint: config + @fusesoc --cores-root . run --setup --build --build-root rvb_out --target lint --tool verilator socet:riscv:RISCVBusiness + @echo "Lint finished, no errors found" + clean: rm -rf build rm -rf rvb_out diff --git a/source_code/include/priv_1_12_internal_if.vh b/source_code/include/priv_1_12_internal_if.vh index 11d482a57..c2de35802 100644 --- a/source_code/include/priv_1_12_internal_if.vh +++ b/source_code/include/priv_1_12_internal_if.vh @@ -37,11 +37,11 @@ interface priv_1_12_internal_if; logic ex_rmgmt; logic [$clog2(`NUM_EXTENSIONS)-1:0] ex_rmgmt_cause; - priv_level_t curr_priv; // Current process privilege + priv_level_t curr_privilege_level; // Current process privilege // CSR block values csr_addr_t csr_addr; // CSR address to read - logic csr_write, csr_set, csr_clear; // Is the CSR currently being modified? + logic csr_write, csr_set, csr_clear, csr_read_only; // Is the CSR currently being modified? logic invalid_csr; // Bad CSR address logic inst_ret; // signal when an instruction is retired word_t new_csr_val, old_csr_val; // new and old CSR values (atomically swapped) @@ -69,15 +69,15 @@ interface priv_1_12_internal_if; mstatus_t curr_mstatus, next_mstatus; mtvec_t curr_mtvec; csr_reg_t curr_mtval, next_mtval; - logic inject_mip, inject_mie, inject_mcause, inject_mepc, inject_mstatus, inject_mtval; + logic inject_mip, inject_mcause, inject_mepc, inject_mstatus, inject_mtval; // Things from the pipe we care about word_t epc; // pc of the instruction prior to the exception word_t priv_pc; // pc to handle the interrupt/exception logic pipe_clear; // is the pipeline clear of hazards logic insert_pc; // inform pipeline that we are changing the PC - logic mret, sret, uret; // returning from a trap instruction - logic intr; // Did something trigger an interrupt? + logic mret, sret; // returning from a trap instruction + logic intr; // Did something trigger an interrupt or exception? // Addresses and memory access info for memory protection logic [RAM_ADDR_SIZE-1:0] daddr, iaddr; // Address to check @@ -91,8 +91,8 @@ interface priv_1_12_internal_if; logic pmp_s_fault, pmp_l_fault, pmp_i_fault; // PMP store fault, load fault, instruction fault modport csr ( - input csr_addr, curr_priv, csr_write, csr_set, csr_clear, new_csr_val, inst_ret, valid_write, - inject_mcause, inject_mepc, inject_mie, inject_mip, inject_mstatus, inject_mtval, + input csr_addr, curr_privilege_level, csr_write, csr_set, csr_clear, csr_read_only, new_csr_val, inst_ret, valid_write, + inject_mcause, inject_mepc, inject_mip, inject_mstatus, inject_mtval, next_mcause, next_mepc, next_mie, next_mip, next_mstatus, next_mtval, output old_csr_val, invalid_csr, curr_mcause, curr_mepc, curr_mie, curr_mip, curr_mstatus, curr_mtvec @@ -103,13 +103,13 @@ interface priv_1_12_internal_if; clear_timer_int_u, clear_timer_int_s, clear_timer_int_m, clear_soft_int_u, clear_soft_int_s, clear_soft_int_m, clear_ext_int_u, clear_ext_int_s, clear_ext_int_m, mal_insn, fault_insn_access, illegal_insn, breakpoint, fault_l, mal_l, fault_s, mal_s, env_u, env_s, env_m, fault_insn_page, fault_load_page, fault_store_page, curr_mcause, curr_mepc, curr_mie, curr_mip, curr_mstatus, curr_mtval, - mret, sret, uret, pipe_clear, ex_rmgmt, ex_rmgmt_cause, epc, - output inject_mcause, inject_mepc, inject_mie, inject_mip, inject_mstatus, inject_mtval, + mret, sret, pipe_clear, ex_rmgmt, ex_rmgmt_cause, epc, curr_privilege_level, + output inject_mcause, inject_mepc, inject_mip, inject_mstatus, inject_mtval, next_mcause, next_mepc, next_mie, next_mip, next_mstatus, next_mtval, intr ); modport pipe_ctrl ( - input intr, pipe_clear, mret, sret, uret, curr_mtvec, curr_mcause, curr_mepc, + input intr, pipe_clear, mret, sret, curr_mtvec, curr_mepc, next_mcause, output insert_pc, priv_pc ); @@ -119,10 +119,15 @@ interface priv_1_12_internal_if; ); modport pmp ( - input iaddr, daddr, ren, wen, xen, curr_priv, curr_mstatus, + input iaddr, daddr, ren, wen, xen, curr_privilege_level, curr_mstatus, output pmp_s_fault, pmp_i_fault, pmp_l_fault ); + modport mode ( + input mret, curr_mstatus, intr, + output curr_privilege_level + ); + endinterface `endif // PRIV_1_12_INTERNAL_IF_VH diff --git a/source_code/include/prv_pipeline_if.vh b/source_code/include/prv_pipeline_if.vh index 52657b45e..dee0456c0 100644 --- a/source_code/include/prv_pipeline_if.vh +++ b/source_code/include/prv_pipeline_if.vh @@ -34,7 +34,7 @@ interface prv_pipeline_if(); // exception signals logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m, ret; + breakpoint, env, ret, wfi; // interrupt signals logic timer_int, soft_int, ext_int; @@ -45,8 +45,8 @@ interface prv_pipeline_if(); word_t [3:0] xtvec, xepc_r; // csr rw - logic swap, clr, set; - logic invalid_csr, valid_write; + logic swap, clr, set, read_only; + logic invalid_priv_isn, valid_write; csr_addr_t csr_addr; word_t rdata, wdata; @@ -67,13 +67,13 @@ interface prv_pipeline_if(); input priv_pc, insert_pc, intr, output pipe_clear, ret, epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m, badaddr, wb_enable, + breakpoint, env, badaddr, wb_enable, ex_rmgmt, ex_rmgmt_cause ); modport pipe ( - output swap, clr, set, wdata, csr_addr, valid_write, instr, dren, dwen, daddr, d_acc_width, - input rdata, invalid_csr, prot_fault_s, prot_fault_l + output swap, clr, set, read_only, wdata, csr_addr, valid_write, instr, dren, dwen, daddr, d_acc_width, wfi, + input rdata, invalid_priv_isn, prot_fault_s, prot_fault_l ); modport fetch ( @@ -84,12 +84,12 @@ interface prv_pipeline_if(); modport priv_block ( input pipe_clear, ret, epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m, badaddr, swap, clr, set, + breakpoint, env, badaddr, swap, clr, set, read_only, wfi, wdata, csr_addr, valid_write, wb_enable, instr, ex_rmgmt, ex_rmgmt_cause, daddr, iaddr, dren, dwen, iren, d_acc_width, i_acc_width, - output priv_pc, insert_pc, intr, rdata, invalid_csr, + output priv_pc, insert_pc, intr, rdata, invalid_priv_isn, prot_fault_s, prot_fault_l, prot_fault_i ); diff --git a/source_code/include/tspp_hazard_unit_if.vh b/source_code/include/tspp_hazard_unit_if.vh index f7f9f3e7c..4e2ca77d5 100644 --- a/source_code/include/tspp_hazard_unit_if.vh +++ b/source_code/include/tspp_hazard_unit_if.vh @@ -34,7 +34,7 @@ interface tspp_hazard_unit_if(); //Pipeline Exceptions logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m; + breakpoint, env; word_t epc_f, epc_e, badaddr_f, badaddr_e; // TVEC Insertion @@ -51,7 +51,7 @@ interface tspp_hazard_unit_if(); input i_mem_busy, d_mem_busy, dren, dwen, jump, branch, mispredict, halt, pc,fault_insn, mal_insn, illegal_insn, fault_l, - mal_l, fault_s, mal_s, breakpoint, env_m, ret, + mal_l, fault_s, mal_s, breakpoint, env, ret, epc_f, epc_e, badaddr_f, badaddr_e, token_ex, fence_stall, rv32c_ready, output pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren ); @@ -64,7 +64,7 @@ interface tspp_hazard_unit_if(); modport execute ( input if_ex_stall, npc_sel, output d_mem_busy, dren, dwen, jump, branch, mispredict, halt, - illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, ret, epc_e, + illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env, ret, epc_e, badaddr_e, token_ex, fence_stall ); diff --git a/source_code/packages/machine_mode_types_1_12_pkg.sv b/source_code/packages/machine_mode_types_1_12_pkg.sv index b10970898..aa2e12e7c 100644 --- a/source_code/packages/machine_mode_types_1_12_pkg.sv +++ b/source_code/packages/machine_mode_types_1_12_pkg.sv @@ -161,6 +161,74 @@ package machine_mode_types_1_12_pkg; MHPMEVENT31_ADDR = 12'h33F } maddr_t; + /* User Mode Addresses */ + typedef enum logic [11:0] { + CYCLE_ADDR = 12'hC00, + TIME_ADDR = 12'hC01, + INSTRET_ADDR = 12'hC02, + HPMCOUNTER3_ADDR = 12'hC03, + HPMCOUNTER4_ADDR = 12'hC04, + HPMCOUNTER5_ADDR = 12'hC05, + HPMCOUNTER6_ADDR = 12'hC06, + HPMCOUNTER7_ADDR = 12'hC07, + HPMCOUNTER8_ADDR = 12'hC08, + HPMCOUNTER9_ADDR = 12'hC09, + HPMCOUNTER10_ADDR = 12'hC0A, + HPMCOUNTER11_ADDR = 12'hC0B, + HPMCOUNTER12_ADDR = 12'hC0C, + HPMCOUNTER13_ADDR = 12'hC0D, + HPMCOUNTER14_ADDR = 12'hC0E, + HPMCOUNTER15_ADDR = 12'hC0F, + HPMCOUNTER16_ADDR = 12'hC10, + HPMCOUNTER17_ADDR = 12'hC11, + HPMCOUNTER18_ADDR = 12'hC12, + HPMCOUNTER19_ADDR = 12'hC13, + HPMCOUNTER20_ADDR = 12'hC14, + HPMCOUNTER21_ADDR = 12'hC15, + HPMCOUNTER22_ADDR = 12'hC16, + HPMCOUNTER23_ADDR = 12'hC17, + HPMCOUNTER24_ADDR = 12'hC18, + HPMCOUNTER25_ADDR = 12'hC19, + HPMCOUNTER26_ADDR = 12'hC1A, + HPMCOUNTER27_ADDR = 12'hC1B, + HPMCOUNTER28_ADDR = 12'hC1C, + HPMCOUNTER29_ADDR = 12'hC1D, + HPMCOUNTER30_ADDR = 12'hC1E, + HPMCOUNTER31_ADDR = 12'hC1F, + CYCLEH_ADDR = 12'hC80, + INSTRETH_ADDR = 12'hC81, + TIMEH_ADDR = 12'hC82, + HPMCOUNTER3H_ADDR = 12'hC83, + HPMCOUNTER4H_ADDR = 12'hC84, + HPMCOUNTER5H_ADDR = 12'hC85, + HPMCOUNTER6H_ADDR = 12'hC86, + HPMCOUNTER7H_ADDR = 12'hC87, + HPMCOUNTER8H_ADDR = 12'hC88, + HPMCOUNTER9H_ADDR = 12'hC89, + HPMCOUNTER10H_ADDR = 12'hC8A, + HPMCOUNTER11H_ADDR = 12'hC8B, + HPMCOUNTER12H_ADDR = 12'hC8C, + HPMCOUNTER13H_ADDR = 12'hC8D, + HPMCOUNTER14H_ADDR = 12'hC8E, + HPMCOUNTER15H_ADDR = 12'hC8F, + HPMCOUNTER16H_ADDR = 12'hC90, + HPMCOUNTER17H_ADDR = 12'hC91, + HPMCOUNTER18H_ADDR = 12'hC92, + HPMCOUNTER19H_ADDR = 12'hC93, + HPMCOUNTER20H_ADDR = 12'hC94, + HPMCOUNTER21H_ADDR = 12'hC95, + HPMCOUNTER22H_ADDR = 12'hC96, + HPMCOUNTER23H_ADDR = 12'hC97, + HPMCOUNTER24H_ADDR = 12'hC98, + HPMCOUNTER25H_ADDR = 12'hC99, + HPMCOUNTER26H_ADDR = 12'hC9A, + HPMCOUNTER27H_ADDR = 12'hC9B, + HPMCOUNTER28H_ADDR = 12'hC9C, + HPMCOUNTER29H_ADDR = 12'hC9D, + HPMCOUNTER30H_ADDR = 12'hC9E, + HPMCOUNTER31H_ADDR = 12'hC9F + } uaddr_t; + /* Machine Mode Register Types */ diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index 97234a885..a33ee78de 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -73,6 +73,7 @@ module tspp_execute_stage ( ); assign wfi = cu_if.wfi; //Added by rkannank + assign prv_pipe_if.wfi = cu_if.wfi; generate if (BASE_ISA == "RV32E") begin : g_rfile_select @@ -362,6 +363,7 @@ module tspp_execute_stage ( assign prv_pipe_if.swap = cu_if.csr_swap; assign prv_pipe_if.clr = cu_if.csr_clr; assign prv_pipe_if.set = cu_if.csr_set; + assign prv_pipe_if.read_only = (rf_if.rs1 == '0) || (cu_if.zimm == '0); // See Zicsr docs for more info assign prv_pipe_if.wdata = cu_if.csr_imm ? {27'h0, cu_if.zimm} : rf_if.rs1_data; assign prv_pipe_if.csr_addr = cu_if.csr_addr; assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr | @@ -377,13 +379,13 @@ module tspp_execute_stage ( //Send exceptions to Hazard Unit assign hazard_if.illegal_insn = (cu_if.illegal_insn & ~rm_if.ex_token) - | prv_pipe_if.invalid_csr; + | prv_pipe_if.invalid_priv_isn; assign hazard_if.fault_l = prv_pipe_if.prot_fault_l; assign hazard_if.mal_l = cu_if.dren & mal_addr; assign hazard_if.fault_s = prv_pipe_if.prot_fault_s; assign hazard_if.mal_s = cu_if.dwen & mal_addr; assign hazard_if.breakpoint = cu_if.breakpoint; - assign hazard_if.env_m = cu_if.ecall_insn; + assign hazard_if.env = cu_if.ecall_insn; assign hazard_if.ret = cu_if.ret_insn; assign hazard_if.badaddr_e = dgen_bus_if.addr; diff --git a/source_code/pipelines/tspp/tspp_hazard_unit.sv b/source_code/pipelines/tspp/tspp_hazard_unit.sv index 4d4f89164..3022289f5 100644 --- a/source_code/pipelines/tspp/tspp_hazard_unit.sv +++ b/source_code/pipelines/tspp/tspp_hazard_unit.sv @@ -78,7 +78,7 @@ module tspp_hazard_unit ( assign prv_pipe_if.ret = hazard_if.ret; assign e_ex_stage = hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l | hazard_if.fault_s | hazard_if.mal_s | hazard_if.breakpoint | - hazard_if.env_m; + hazard_if.env; assign e_f_stage = hazard_if.fault_insn | hazard_if.mal_insn; assign intr = ~e_ex_stage & ~e_f_stage & prv_pipe_if.intr; @@ -103,7 +103,7 @@ module tspp_hazard_unit ( assign prv_pipe_if.fault_s = hazard_if.fault_s; assign prv_pipe_if.mal_s = hazard_if.mal_s; assign prv_pipe_if.breakpoint = hazard_if.breakpoint; - assign prv_pipe_if.env_m = hazard_if.env_m; + assign prv_pipe_if.env = hazard_if.env; assign prv_pipe_if.ex_rmgmt = rm_if.exception; assign prv_pipe_if.ex_rmgmt_cause = rm_if.ex_cause; diff --git a/source_code/privs/priv.core b/source_code/privs/priv.core index ca646fd0a..34807d03a 100644 --- a/source_code/privs/priv.core +++ b/source_code/privs/priv.core @@ -13,6 +13,7 @@ filesets: - priv_1_12/priv_1_12_pma.sv - priv_1_12/priv_1_12_pmp.sv - priv_1_12/priv_1_12_pmp_matcher.sv + - priv_1_12/priv_1_12_mode.sv file_type: systemVerilogSource diff --git a/source_code/privs/priv_1_12/priv_1_12_block.sv b/source_code/privs/priv_1_12/priv_1_12_block.sv index 55b2423f6..f566c3bfd 100644 --- a/source_code/privs/priv_1_12/priv_1_12_block.sv +++ b/source_code/privs/priv_1_12/priv_1_12_block.sv @@ -29,6 +29,7 @@ module priv_1_12_block ( input logic CLK, nRST, + input logic [63:0] mtime, prv_pipeline_if.priv_block prv_pipe_if, core_interrupt_if.core interrupt_if ); @@ -39,15 +40,12 @@ module priv_1_12_block ( priv_ext_if priv_ext_pma_if(); priv_ext_if priv_ext_pmp_if(); - priv_1_12_csr csr (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_pma_if(priv_ext_pma_if), .priv_ext_pmp_if(priv_ext_pmp_if)); + priv_1_12_csr csr (.CLK(CLK), .nRST(nRST), .mtime(mtime), .prv_intern_if(prv_intern_if), .priv_ext_pma_if(priv_ext_pma_if), .priv_ext_pmp_if(priv_ext_pmp_if)); priv_1_12_int_ex_handler int_ex_handler (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if)); priv_1_12_pipe_control pipe_ctrl (.prv_intern_if(prv_intern_if)); priv_1_12_pma pma (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_if(priv_ext_pma_if)); priv_1_12_pmp pmp (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_if(priv_ext_pmp_if)); - - priv_level_t curr_priv; - - assign prv_intern_if.curr_priv = M_MODE; // TODO make this changeable + priv_1_12_mode mode (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if)); // Assign CSR values assign prv_intern_if.inst_ret = prv_pipe_if.wb_enable & prv_pipe_if.instr; @@ -55,9 +53,11 @@ module priv_1_12_block ( assign prv_intern_if.csr_write = prv_pipe_if.swap; assign prv_intern_if.csr_clear = prv_pipe_if.clr; assign prv_intern_if.csr_set = prv_pipe_if.set; + assign prv_intern_if.csr_read_only = prv_pipe_if.read_only; assign prv_intern_if.new_csr_val = prv_pipe_if.wdata; assign prv_pipe_if.rdata = prv_intern_if.old_csr_val; - assign prv_pipe_if.invalid_csr = prv_intern_if.invalid_csr; + assign prv_pipe_if.invalid_priv_isn = prv_intern_if.invalid_csr | (prv_pipe_if.ret & (prv_intern_if.curr_privilege_level != M_MODE)) + | (prv_pipe_if.wfi & (prv_intern_if.curr_privilege_level == U_MODE) & (prv_intern_if.curr_mstatus.tw)); // Disable interrupts that will not be used assign prv_intern_if.timer_int_u = 1'b0; @@ -92,17 +92,16 @@ module priv_1_12_block ( assign prv_intern_if.fault_s = prv_pipe_if.fault_s; assign prv_intern_if.mal_s = prv_pipe_if.mal_s; assign prv_intern_if.breakpoint = prv_pipe_if.breakpoint; - assign prv_intern_if.env_m = prv_pipe_if.env_m; + assign prv_intern_if.env_m = prv_pipe_if.env && (prv_intern_if.curr_privilege_level == M_MODE); assign prv_intern_if.env_s = 1'b0; - assign prv_intern_if.env_u = 1'b0; + assign prv_intern_if.env_u = prv_pipe_if.env && (prv_intern_if.curr_privilege_level == U_MODE); assign prv_intern_if.fault_insn_page = 1'b0; assign prv_intern_if.fault_load_page = 1'b0; assign prv_intern_if.fault_store_page = 1'b0; assign prv_intern_if.curr_mtval = prv_pipe_if.badaddr; assign prv_intern_if.valid_write = prv_pipe_if.valid_write; - assign prv_intern_if.mret = prv_pipe_if.ret; // TODO make this changeable + assign prv_intern_if.mret = prv_pipe_if.ret & (prv_intern_if.curr_privilege_level == M_MODE); assign prv_intern_if.sret = 1'b0; - assign prv_intern_if.uret = 1'b0; // RISC-MGMT? // not sure what these are for, part of priv 1.11 diff --git a/source_code/privs/priv_1_12/priv_1_12_csr.sv b/source_code/privs/priv_1_12/priv_1_12_csr.sv index 2958c0c59..92dd66a1d 100644 --- a/source_code/privs/priv_1_12/priv_1_12_csr.sv +++ b/source_code/privs/priv_1_12/priv_1_12_csr.sv @@ -31,6 +31,7 @@ module priv_1_12_csr #( )( input CLK, input nRST, + input logic [63:0] mtime, priv_1_12_internal_if.csr prv_intern_if, priv_ext_if.priv priv_ext_pma_if, priv_ext_if.priv priv_ext_pmp_if @@ -77,31 +78,31 @@ module priv_1_12_csr #( csr_reg_t nxt_csr_val; // invalid_csr flags - logic invalid_csr_0, invalid_csr_1; // 0: lack of privilege, 1: bad address - assign prv_intern_if.invalid_csr = invalid_csr_0 | invalid_csr_1; + logic invalid_csr_priv, invalid_csr_addr; + assign prv_intern_if.invalid_csr = invalid_csr_priv | invalid_csr_addr; + + // csr operation flag + logic csr_operation; + assign csr_operation = prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear; // Extension Broadcast Signals // - PMA assign priv_ext_pma_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_pma_if.value_in = nxt_csr_val; - assign priv_ext_pma_if.csr_active = ~invalid_csr_0 & prv_intern_if.valid_write - & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); + assign priv_ext_pma_if.csr_active = ~invalid_csr_priv & prv_intern_if.valid_write & (csr_operation); // - PMP assign priv_ext_pmp_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_pmp_if.value_in = nxt_csr_val; - assign priv_ext_pmp_if.csr_active = ~invalid_csr_0 & prv_intern_if.valid_write - & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); + assign priv_ext_pmp_if.csr_active = ~invalid_csr_priv & prv_intern_if.valid_write & (csr_operation); `ifdef RV32F_SUPPORTED assign priv_ext_f_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_f_if.value_in = nxt_csr_val; - assign priv_ext_f_if.csr_active = prv_intern_if.valid_write - & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); + assign priv_ext_f_if.csr_active = ~invalid_csr_priv & prv_intern_if.valid_write & (csr_operation); `endif // RV32F_SUPPORTED `ifdef RV32V_SUPPORTED assign priv_ext_v_if.csr_addr = prv_intern_if.csr_addr; assign priv_ext_v_if.value_in = nxt_csr_val; - assign priv_ext_v_if.csr_active = prv_intern_if.valid_write - & (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear); + assign priv_ext_v_if.csr_active = ~invalid_csr_priv & prv_intern_if.valid_write & (csr_operation); `endif // RV32V_SUPPORTED /* Save some logic with this */ @@ -122,6 +123,7 @@ module priv_1_12_csr #( assign misa.base = BASE_RV32; // NOTE: Per the v1.12 spec, both I and E CANNOT be high - If supporting E, I must be disabled assign misa.extensions = MISA_EXT_I + | MISA_EXT_U `ifdef RV32C_SUPPORTED | MISA_EXT_C `endif `ifdef RV32E_SUPPORTED @@ -130,8 +132,6 @@ module priv_1_12_csr #( | MISA_EXT_F `endif `ifdef RV32M_SUPPORTED | MISA_EXT_M - `endif `ifdef RV32U_SUPPORTED - | MISA_EXT_U `endif `ifdef RV32V_SUPPORTED | MISA_EXT_V `endif `ifdef CUSTOM_SUPPORTED @@ -151,7 +151,7 @@ module priv_1_12_csr #( /* mstatus reset */ mstatus.mie <= 1'b0; mstatus.mpie <= 1'b0; - mstatus.mpp <= M_MODE; + mstatus.mpp <= U_MODE; mstatus.mprv <= 1'b0; mstatus.tw <= 1'b1; mstatus.reserved_0 <= '0; @@ -166,7 +166,7 @@ module priv_1_12_csr #( mstatus.mxr <= 1'b0; mstatus.tvm <= 1'b0; mstatus.tsr <= 1'b0; - mstatus.sd <= &(mstatus.vs) | &(mstatus.fs) | &(mstatus.xs); + mstatus.sd <= 1'b0; `ifdef RV32V_SUPPORTED mstatus.vs <= VS_INITIAL; `else @@ -230,6 +230,7 @@ module priv_1_12_csr #( end // Privilege Check and Legal Value Check + logic inject_mcycle, inject_minstret, inject_mcycleh, inject_minstreth; always_comb begin mstatus_next = mstatus; mtvec_next = mtvec; @@ -242,21 +243,30 @@ module priv_1_12_csr #( mcounterinhibit_next = mcounterinhibit; mcause_next = mcause; + inject_mcycle = 1'b0; + inject_mcycle = 1'b0; + inject_minstret = 1'b0; + inject_minstreth = 1'b0; + nxt_csr_val = (prv_intern_if.csr_write) ? prv_intern_if.new_csr_val : (prv_intern_if.csr_set) ? prv_intern_if.new_csr_val | prv_intern_if.old_csr_val : (prv_intern_if.csr_clear) ? ~prv_intern_if.new_csr_val & prv_intern_if.old_csr_val : prv_intern_if.new_csr_val; - invalid_csr_0 = 1'b0; + invalid_csr_priv = 1'b0; - if (prv_intern_if.csr_addr[9:8] > prv_intern_if.curr_priv) begin - if (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear) begin - invalid_csr_0 = 1'b1; // Not enough privilege + if (prv_intern_if.csr_addr[11:10] == 2'b11 && !prv_intern_if.csr_read_only) begin + if (csr_operation) begin + invalid_csr_priv = 1'b1; // Attempting to modify a R/O CSR + end + end else if (prv_intern_if.csr_addr[9:8] > prv_intern_if.curr_privilege_level) begin + if (csr_operation) begin + invalid_csr_priv = 1'b1; // Not enough privilege end end else begin if (prv_intern_if.valid_write) begin casez(prv_intern_if.csr_addr) MSTATUS_ADDR: begin - if (prv_intern_if.new_csr_val[12:11] == RESERVED_MODE) begin + if (prv_intern_if.new_csr_val[12:11] == RESERVED_MODE || prv_intern_if.new_csr_val[12:11] == S_MODE) begin mstatus_next.mpp = U_MODE; // If invalid privilege level, dump at 0 end else begin mstatus_next.mpp = priv_level_t'(nxt_csr_val[12:11]); @@ -305,6 +315,18 @@ module priv_1_12_csr #( MCAUSE_ADDR: begin mcause_next = nxt_csr_val; end + MCYCLE_ADDR: begin + inject_mcycle = 1'b1; + end + MINSTRET_ADDR: begin + inject_minstret = 1'b1; + end + MCYCLEH_ADDR: begin + inject_mcycleh = 1'b1; + end + MINSTRETH_ADDR: begin + inject_minstreth = 1'b1; + end endcase end end @@ -322,12 +344,11 @@ module priv_1_12_csr #( if (prv_intern_if.inject_mcause) begin mcause_next = prv_intern_if.next_mcause; end - if (prv_intern_if.inject_mie) begin - mie_next = prv_intern_if.next_mie; - end if (prv_intern_if.inject_mip) begin mip_next = prv_intern_if.next_mip; end + + mstatus_next.sd = &(mstatus_next.vs) | &(mstatus_next.fs) | &(mstatus_next.xs); end // hw perf mon @@ -335,20 +356,35 @@ module priv_1_12_csr #( cf_next = cycles_full; if_next = instret_full; - if (mcounteren.cy & ~mcounterinhibit.cy) begin + if (~mcounterinhibit.cy) begin cf_next = cycles_full + 1; end - if (mcounteren.ir & ~mcounterinhibit.ir) begin + if (~mcounterinhibit.ir) begin if_next = instret_full + prv_intern_if.inst_ret; end + + if (inject_mcycle) begin + cf_next = {mcycleh, nxt_csr_val}; + end + if (inject_mcycleh) begin + cf_next = {nxt_csr_val, mcycle}; + end + + if (inject_minstret) begin + if_next = {minstreth, nxt_csr_val}; + end + if (inject_minstreth) begin + if_next = {nxt_csr_val, minstret}; + end end - // Return proper values to CPU, PMP, PMA + // Return proper values to CPU always_comb begin /* CPU return */ prv_intern_if.old_csr_val = '0; - invalid_csr_1 = 1'b0; + invalid_csr_addr = 1'b0; casez(prv_intern_if.csr_addr) + /* Machine Mode Addresses */ MVENDORID_ADDR: prv_intern_if.old_csr_val = mvendorid; MARCHID_ADDR: prv_intern_if.old_csr_val = marchid; MIMPID_ADDR: prv_intern_if.old_csr_val = mimpid; @@ -370,27 +406,70 @@ module priv_1_12_csr #( MINSTRET_ADDR: prv_intern_if.old_csr_val = minstret; MCYCLEH_ADDR: prv_intern_if.old_csr_val = mcycleh; MINSTRETH_ADDR: prv_intern_if.old_csr_val = minstreth; + /* Unprivileged Addresses */ + CYCLE_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.cy) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = mcycle; + end + end + CYCLEH_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.cy) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = mcycleh; + end + end + INSTRET_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.ir) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = minstret; + end + end + INSTRETH_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.ir) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = minstreth; + end + end + TIME_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.tm) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = /* TODO get mtime */ mtime[31:0]; + end + end + TIMEH_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.tm) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = /* TODO get mtimeh */ mtime[63:32]; + end + end + /* Extension Addresses */ default: begin - if (prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear) begin + if (csr_operation) begin if (priv_ext_pma_if.ack) begin prv_intern_if.old_csr_val = priv_ext_pma_if.value_out; - end - if (priv_ext_pmp_if.ack) begin + end else if (priv_ext_pmp_if.ack) begin prv_intern_if.old_csr_val = priv_ext_pmp_if.value_out; end `ifdef RV32F_SUPPORTED - if (priv_ext_f_if.ack) begin + else if (priv_ext_f_if.ack) begin prv_intern_if.old_csr_val = priv_ext_f_if.value_out; end `endif // RV32F_SUPPORTED `ifdef RV32V_SUPPORTED - if (priv_ext_v_if.ack) begin + else if (priv_ext_v_if.ack) begin prv_intern_if.old_csr_val = priv_ext_v_if.value_out; end `endif // RV32V_SUPPORTED // CSR address doesn't exist - invalid_csr_1 = 1'b1 + invalid_csr_addr = 1'b1 & (~priv_ext_pma_if.ack) & (~priv_ext_pma_if.invalid_csr) & (~priv_ext_pmp_if.ack) & (~priv_ext_pmp_if.invalid_csr) `ifdef RV32F_SUPPORTED diff --git a/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv index cb6bb89ce..6c93c13bb 100644 --- a/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv +++ b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv @@ -37,8 +37,7 @@ module priv_1_12_int_ex_handler ( int_code_t int_src; logic interrupt, clear_interrupt; - // NOTE see the bottom for why these signals exist - logic interrupt_reg, interrupt_fired, update_mie; + logic interrupt_fired; // Determine the source of the interrupt always_comb begin @@ -112,17 +111,16 @@ module priv_1_12_int_ex_handler ( end // Output info to pipe_ctrl - assign prv_intern_if.intr = exception | interrupt_reg; + assign prv_intern_if.intr = exception | interrupt_fired; + + // Only output an interrupt if said interrupt is enabled assign interrupt_fired = (prv_intern_if.curr_mstatus.mie & ((prv_intern_if.curr_mie.mtie & prv_intern_if.curr_mip.mtip) | (prv_intern_if.curr_mie.msie & prv_intern_if.curr_mip.msip) | (prv_intern_if.curr_mie.meie & prv_intern_if.curr_mip.meip))); // Register updates on Interrupts/Exceptions - /* NOTE a lot of the below code are patterns that exist to solve issues - on the 2 stage pipeline. This should be reimagined if we change pipelines. - Most of this is copied verbatim from the priv 1.11 unit */ - assign prv_intern_if.inject_mcause = exception | interrupt; + assign prv_intern_if.inject_mcause = exception | interrupt_fired; assign prv_intern_if.next_mcause.interrupt = ~exception; assign prv_intern_if.next_mcause.cause = exception ? ex_src : int_src; @@ -149,12 +147,12 @@ module priv_1_12_int_ex_handler ( else if (prv_intern_if.clear_timer_int_s) prv_intern_if.next_mip.stip = 1'b0; end - assign prv_intern_if.inject_mstatus = exception | prv_intern_if.intr | prv_intern_if.mret; + assign prv_intern_if.inject_mstatus = prv_intern_if.intr | prv_intern_if.mret; always_comb begin prv_intern_if.next_mstatus = prv_intern_if.curr_mstatus; // interrupt has truly been registered and it is time to go to the vector table - if (update_mie) begin + if (prv_intern_if.intr) begin // when a trap is taken mpie is set to the current mie prv_intern_if.next_mstatus.mpie = prv_intern_if.curr_mstatus.mie; prv_intern_if.next_mstatus.mie = 1'b0; @@ -162,60 +160,29 @@ module priv_1_12_int_ex_handler ( prv_intern_if.next_mstatus.mpie = 1'b0; // leaving the vector table prv_intern_if.next_mstatus.mie = prv_intern_if.curr_mstatus.mpie; end + + // We need to change mstatus bits for mode changes + if (prv_intern_if.intr) begin // If we are receiving an exception or interrupt + prv_intern_if.next_mstatus.mpp = prv_intern_if.curr_privilege_level; + end else if (prv_intern_if.mret) begin // If we are going back from a trap + prv_intern_if.next_mstatus.mpp = U_MODE; // We must set mpp to the least privileged mode possible + if (prv_intern_if.curr_mstatus.mpp != M_MODE) begin + prv_intern_if.next_mstatus.mprv = 1'b0; + end + end end - // Update EPC as soon as interrupt or exception is found - // Note: mepc cannot update immediately, as if the processor is in an interrupt already, - // the MEPC captured will be within the interrupt (and nested interrupts are not supported). - // Interrupt fired notes when an interrupt is seen by the processor, i.e. when mstatus.mie is high again. - // The signal is 2 cycles long, so the update_mie signal is used to clip it down to 1 to prevent MEPC - // double update which results in skipping an instruction. - // TODO: Change to interrupt - assign prv_intern_if.inject_mepc = exception | (interrupt_fired & ~update_mie); + assign prv_intern_if.inject_mepc = exception | interrupt_fired; assign prv_intern_if.next_mepc = prv_intern_if.epc; - // TODO: May need to insert other exception signals assign prv_intern_if.inject_mtval = (prv_intern_if.mal_l | prv_intern_if.fault_l | prv_intern_if.mal_s | prv_intern_if.fault_s | prv_intern_if.illegal_insn | prv_intern_if.fault_insn_access - | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) - & prv_intern_if.pipe_clear; + | prv_intern_if.mal_insn + | prv_intern_if.breakpoint + | prv_intern_if.ex_rmgmt) + & prv_intern_if.pipe_clear; assign prv_intern_if.next_mtval = prv_intern_if.curr_mtval; - /* Interrupt needs to be latched until pipeline cleared */ - /* because mstatus.ie causes the irq to disappear after */ - /* one cycle. Cannot wait to clear mstatus.ie because */ - /* then another interrupt can fire during pipeline clear */ - always_ff @ (posedge CLK, negedge nRST) begin - if (!nRST) - interrupt_reg <= '0; - else if (interrupt_fired) - interrupt_reg <= 1'b1; - else if (prv_intern_if.pipe_clear) - interrupt_reg <= '0; - end - - /* - * Fix for MIE/MPIE issue. This used to be the same as 'interrupt_reg' above, - * but the above stays high for 2+ cycles (i.e. waiting for pipe_clear). - * This caused MPIE to update twice; the first update would set MPIE to 1, - * and the second would cause MPIE to return to 0. Then, after an MRET, - * MIE would not be restored since MPIE was lost. Additionally, shortening - * interrupt_reg was not an option since pipe_clear must be asserted for the - * PC to be inserted into the pipeline from the priv unit, so creating this - * extra register was the cleanest solution to ensuring MPIE updates exactly - * once. - */ - always_ff @(posedge CLK, negedge nRST) begin - if(!nRST) - update_mie <= '0; - else if(interrupt_fired && ~update_mie) - update_mie <= 1'b1; - else if (prv_intern_if.pipe_clear) - update_mie <= '0; - else - update_mie <= '0; - end - endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_mode.sv b/source_code/privs/priv_1_12/priv_1_12_mode.sv new file mode 100644 index 000000000..13f72505c --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_mode.sv @@ -0,0 +1,59 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_mode.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 11/16/2022 +* Description: Processor privilege mode switcher +*/ + +`include "prv_pipeline_if.vh" +`include "priv_1_12_internal_if.vh" +`include "core_interrupt_if.vh" +`include "priv_ext_if.vh" + +module priv_1_12_mode ( + input logic CLK, nRST, + priv_1_12_internal_if.mode prv_intern_if +); + + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + priv_level_t curr_priv_level, next_priv_level; + + always_ff @ (posedge CLK, negedge nRST) begin + if (~nRST) begin + curr_priv_level <= M_MODE; + end else begin + curr_priv_level <= next_priv_level; + end + end + + always_comb begin + next_priv_level = curr_priv_level; + if (prv_intern_if.intr) begin + next_priv_level = M_MODE; + end else if (prv_intern_if.mret) begin + next_priv_level = prv_intern_if.curr_mstatus.mpp; + end + end + + assign prv_intern_if.curr_privilege_level = curr_priv_level; + +endmodule \ No newline at end of file diff --git a/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv b/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv index 33b7d0e4b..575a33f36 100644 --- a/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv +++ b/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv @@ -32,16 +32,15 @@ module priv_1_12_pipe_control ( import machine_mode_types_1_12_pkg::*; import rv32i_types_pkg::*; - assign prv_intern_if.insert_pc = prv_intern_if.mret | prv_intern_if.sret | prv_intern_if.uret | - (prv_intern_if.pipe_clear & prv_intern_if.intr); + assign prv_intern_if.insert_pc = prv_intern_if.mret | prv_intern_if.sret | prv_intern_if.intr; always_comb begin prv_intern_if.priv_pc = '0; if (prv_intern_if.intr) begin - if (prv_intern_if.curr_mtvec.mode == VECTORED & prv_intern_if.curr_mcause.interrupt) begin + if (prv_intern_if.curr_mtvec.mode == VECTORED & prv_intern_if.next_mcause.interrupt) begin prv_intern_if.priv_pc = (prv_intern_if.curr_mtvec.base << 2) - + (prv_intern_if.curr_mcause.cause << 2); + + (prv_intern_if.next_mcause.cause << 2); end else begin prv_intern_if.priv_pc = prv_intern_if.curr_mtvec.base << 2; end diff --git a/source_code/privs/priv_1_12/priv_1_12_pmp.sv b/source_code/privs/priv_1_12/priv_1_12_pmp.sv index 77744cd88..b364be65d 100644 --- a/source_code/privs/priv_1_12/priv_1_12_pmp.sv +++ b/source_code/privs/priv_1_12/priv_1_12_pmp.sv @@ -178,7 +178,7 @@ module priv_1_12_pmp ( default: d_match_found = 1'b0; endcase - if (prv_intern_if.curr_priv != M_MODE || (prv_intern_if.curr_mstatus.mprv && prv_intern_if.curr_mstatus.mpp != M_MODE)) begin // Core is in an unprivileged state or needs privilege checks + if (prv_intern_if.curr_privilege_level != M_MODE || (prv_intern_if.curr_mstatus.mprv && prv_intern_if.curr_mstatus.mpp != M_MODE)) begin // Core is in an unprivileged state or needs privilege checks if (~d_match_found) begin d_prot_fault = 1'b1; end else begin @@ -239,17 +239,17 @@ module priv_1_12_pmp ( default: i_match_found = 1'b0; endcase - if (prv_intern_if.curr_priv != M_MODE || (prv_intern_if.curr_mstatus.mprv && prv_intern_if.curr_mstatus.mpp != M_MODE)) begin // Core is in an unprivileged state or needs privilege checks + if (prv_intern_if.curr_privilege_level != M_MODE || (prv_intern_if.curr_mstatus.mprv && prv_intern_if.curr_mstatus.mpp != M_MODE)) begin // Core is in an unprivileged state or needs privilege checks if (~i_match_found) begin i_prot_fault = 1'b1; end else begin - if ((prv_intern_if.ren & ~i_match.R) || (prv_intern_if.wen & ~i_match.W)) begin + if (prv_intern_if.xen & ~i_match.X) begin i_prot_fault = 1'b1; end end end else begin // Core is in M_MODE with no privilege check requirements if (d_match_found & i_match.L) begin - if ((prv_intern_if.ren & ~i_match.R) || (prv_intern_if.wen & ~i_match.W)) begin + if (prv_intern_if.xen & ~i_match.X) begin i_prot_fault = 1'b1; end end diff --git a/source_code/privs/priv_wrapper.sv b/source_code/privs/priv_wrapper.sv index ddda7d1e6..0590abaea 100644 --- a/source_code/privs/priv_wrapper.sv +++ b/source_code/privs/priv_wrapper.sv @@ -26,8 +26,8 @@ `include "core_interrupt_if.vh" module priv_wrapper ( - input logic CLK, - nRST, + input logic CLK, nRST, + input logic [63:0] mtime, prv_pipeline_if.priv_block prv_pipe_if, core_interrupt_if.core interrupt_if ); diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index a639296b0..6f3da360a 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -33,8 +33,8 @@ `include "rv32c_if.vh" module RISCVBusiness ( - input logic CLK, - nRST, + input logic CLK, nRST, + input logic [63:0] mtime, output logic wfi, halt, core_interrupt_if.core interrupt_if, @@ -132,7 +132,8 @@ module RISCVBusiness ( .CLK(CLK), .nRST(nRST), .prv_pipe_if(prv_pipe_if), - .interrupt_if + .interrupt_if, + .mtime(mtime) ); risc_mgmt_wrapper rmgmt ( diff --git a/source_code/standard_core/top_core.sv b/source_code/standard_core/top_core.sv index 63b31f469..82ef8eef4 100644 --- a/source_code/standard_core/top_core.sv +++ b/source_code/standard_core/top_core.sv @@ -6,6 +6,7 @@ module top_core #( ) ( input CLK, nRST, + input [63:0] mtime, output wfi, halt, // generic bus if case diff --git a/tb_core.cc b/tb_core.cc index 1c8d568d6..a54609f9b 100644 --- a/tb_core.cc +++ b/tb_core.cc @@ -232,6 +232,7 @@ void reset(Vtop_core& dut, VerilatedFstC& trace) { dut.timer_int_clear = 0; dut.busy = 1; dut.rdata = 0; + dut.mtime = 0; tick(dut, trace); dut.nRST = 0; @@ -261,6 +262,7 @@ int main(int argc, char **argv) { dut.trace(&m_trace, 5); m_trace.open("waveform.fst"); + mtimecmp = 0xFFFFFFFFFFFFFFFF; // Default to a massive value reset(dut, m_trace); while(!dut.halt && sim_time < 100000) { @@ -288,6 +290,8 @@ int main(int argc, char **argv) { dut.busy = 1; } + dut.mtime = sim_time; + tick(dut, m_trace); update_interrupt_signals(dut); } diff --git a/verification/interrupts-exceptions/ex_after_int.c b/verification/interrupts-exceptions/ex_after_int.c new file mode 100644 index 000000000..2d333b307 --- /dev/null +++ b/verification/interrupts-exceptions/ex_after_int.c @@ -0,0 +1,127 @@ + +#include +#include "utility.h" + +extern volatile int flag; + +volatile uint32_t *mtime = (uint32_t *)MTIME_ADDR; +volatile uint32_t *mtimecmp = (uint32_t *)MTIMECMP_ADDR; +volatile uint32_t *mtimecmph = (uint32_t *)MTIMECMPH_ADDR; +volatile uint32_t *msip = (uint32_t *)MSIP_ADDR; +volatile uint32_t *ext_trigger = (uint32_t *)EXT_ADDR_SET; +volatile uint32_t *ext_clear = (uint32_t *)EXT_ADDR_CLEAR; + +// For this test: need to subtract 0xFE from flag to make flag = 1 +// Each handler should be called once. If not, flag will be wrong + +/* + * RISC-V Vector Layout + * 0 - reserved/exception (overlap of interrupt cause & exception cause) + * 1 - S-SW + * 2 - reserved + * 3 - M-SW + * 4 - reserved + * 5 - S-Timer + * 6 - reserved + * 7 - M-Timer + * 8 - reserved + * 9 - S-Ext + * 10- reserved + * 11- M-Ext + */ + +void __attribute__((interrupt)) exception_handler() { + uint32_t mepc, mcause; + asm volatile("csrr %0, mepc" : "=r"(mepc)); + asm volatile("csrr %0, mcause" : "=r"(mcause)); + print("Exception with mepc: "); + put_uint32_hex(mepc); + print(" mcause: "); + put_uint32_hex(mcause); + print("\n"); + mepc += 4; // NOT PORTABLE TO RV32IC + asm volatile("csrw mepc, %0" : : "r"(mepc)); + flag -= 2; +} + +void __attribute__((interrupt)) m_timer_handler() { + uint32_t mepc, mcause; + asm volatile("csrr %0, mepc" : "=r"(mepc)); + asm volatile("csrr %0, mcause" : "=r"(mcause)); + print("Time interrupt with mepc: "); + put_uint32_hex(mepc); + print(" mcause: "); + put_uint32_hex(mcause); + print("\n"); + flag -= 1; + (*mtimecmph) = 0xFF; // setting mtimecmph makes a very large value +} + +void __attribute__((interrupt)) default_handler() { + uint32_t mcause, mepc; + asm volatile("csrr %0, mcause" : "=r"(mcause)); + asm volatile("csrr %0, mepc" : "=r"(mepc)); + print("Hit default handler, mepc: \n"); + put_uint32_hex(mcause); + print(" mcause: "); + put_uint32_hex(mepc); + print("\n"); + flag = 0; + done(); // Go to done and fail test +} // should not end up here, this is a fail! + +// mtvec value MUST be aligned +// Note: .align 2 forces the jumps to be on multiple-of-4 boundaries here, +// which is required for vectored mode which computes the address as +// (mtvec.base + cause) x 4 +// If this test breaks, it may mean that the alignment is wrong, check the +// disassembly! +void __attribute__((naked)) __attribute__((aligned(4))) handler_dispatch() { + asm volatile(".align 2; j exception_handler"); // 0 + asm volatile(".align 2; j default_handler"); // 1 + asm volatile(".align 2; j default_handler"); // 2 + asm volatile(".align 2; j default_handler"); // 3 + asm volatile(".align 2; j default_handler"); // 4 + asm volatile(".align 2; j default_handler"); // 5 + asm volatile(".align 2; j default_handler"); // 6 + asm volatile(".align 2; j m_timer_handler"); // 7 + asm volatile(".align 2; j default_handler"); // 8 + asm volatile(".align 2; j default_handler"); // 9 + asm volatile(".align 2; j default_handler"); // 10 + asm volatile(".align 2; j default_handler"); // 11 +} + +/* + + This test case attempts an exception after an interrupt occurs. + This was to verify an issue with the previous interrupt/exception handler + where an exception after an interrupt while in VECTORED mode would + incorrectly jump to the wrong handler for the exception. + While that issue is most likely resolved, this case is included for verification. + +*/ +int main() { + uint32_t mtvec_value = (uint32_t)handler_dispatch; + mtvec_value |= 1; // set vectored mode + uint32_t mie_value = 0x888; + uint32_t mstatus_value = 0x8; + + + // set mtimecmp away so interrupt doesn't fire immediately + *mtimecmph = 0x00; + *mtimecmp = 0xFF; + + flag = 4; + + // Setup interrupts + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + asm volatile("csrw mie, %0" : : "r" (mie_value)); + // Interrupts active + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + + while((*mtime) < 0xFF); + + asm volatile("ecall"); + + return 0; +} diff --git a/verification/interrupts-exceptions/utility.c b/verification/interrupts-exceptions/utility.c index d89404f7e..17d82f6d1 100644 --- a/verification/interrupts-exceptions/utility.c +++ b/verification/interrupts-exceptions/utility.c @@ -11,11 +11,11 @@ void print(char *string) { void put_uint32_hex(uint32_t x) { char buf[10] = {0}; - + for(int i = 0; i < 8; i++) { uint8_t value = (x & 0xF); if(value >= 10) { - buf[7-i] = (value + 'A'); + buf[7-i] = ((value-10) + 'A'); } else { buf[7-i] = (value + '0'); } diff --git a/verification/interrupts-exceptions/vectored.c b/verification/interrupts-exceptions/vectored.c index ea2dff440..2b531f7fc 100644 --- a/verification/interrupts-exceptions/vectored.c +++ b/verification/interrupts-exceptions/vectored.c @@ -81,6 +81,7 @@ int main() { // set mtimecmp away so interrupt doesn't fire immediately + *mtimecmph = 0x00; *mtimecmp = 0xFF; // Setup interrupts diff --git a/verification/u-mode/build_all.py b/verification/u-mode/build_all.py new file mode 100755 index 000000000..f306e6c5e --- /dev/null +++ b/verification/u-mode/build_all.py @@ -0,0 +1,51 @@ +#! /usr/bin/python3 + +import subprocess +import glob +import os +import pathlib + + +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i', '-mabi=ilp32', '-mcmodel=medany', + '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] + +cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] + + +if not os.path.isfile('./start.S') or not os.path.isfile('link.ld'): + print('Error: Could not find AFTx06.S or link.ld in this directory') + exit(1) + + +for fname in (glob.glob('./*.c') + glob.glob('./*.S')): + if 'start' in fname or 'utility' in fname: + print("Skipping {} as top-level file, appears to be a utility".format(fname)); + continue + print('Compiling {}'.format(fname)) + basename = pathlib.Path(fname).stem + + rv = subprocess.run(compile_cmd + [fname, '-o', basename + '.elf'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + print('Converting {} to binary'.format(fname)) + rv = subprocess.run(cvt_cmd + [basename + '.elf', basename + '.bin']) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + +print( +''' + Finished compilation. Now, pass the '.bin' file corresponding to + the example to run as an argument to 'VbASIC_wrapper' to run an example! +''') diff --git a/verification/u-mode/csr.c b/verification/u-mode/csr.c new file mode 100644 index 000000000..341df724c --- /dev/null +++ b/verification/u-mode/csr.c @@ -0,0 +1,96 @@ +#include +#include "utility.h" + +extern volatile int flag; +extern volatile int done; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc, mtval; + mcause_t mcause; + asm volatile("csrr %0, mepc" : "=r"(mepc)); + asm volatile("csrr %0, mtval" : "=r"(mtval)); + asm volatile("csrr %0, mcause" : "=r"((mcause_t) mcause)); + + print("mepc: "); + put_uint32_hex(mepc); + print("\n"); + + print("mtval: "); + put_uint32_hex(mtval); + print("\n"); + + print("mcause: "); + put_uint32_hex(mcause.interrupt & 0x1); + print(" "); + put_uint32_hex(mcause.ex_code); + print("\n"); + + print("-----\n"); + + if (~mcause.interrupt && mcause.ex_code == 8){ + uint32_t mstatus = 0x1800; // set mpp back to M_MODE + asm volatile("csrs mstatus, %0" : : "r"(mstatus)); + mepc = (uint32_t) &done; + } + else + { + mepc += 4; + flag -= 1; + } + asm volatile("csrw mepc, %0" : : "r"(mepc)); +} + +void __attribute__((noreturn)) user_main(void) { + uint32_t csr_val; + + asm volatile("csrr %0, cycle" : "=r"(csr_val)); + + asm volatile("csrr %0, time" : "=r"(csr_val)); + + asm volatile("ecall"); // ends user_main + + __builtin_unreachable(); + +} + +int main(void) { + // Setup exceptions + uint32_t mtvec_value = (uint32_t) handler; + uint32_t mstatus_value = 0x08; + asm volatile("csrs mstatus, %0" : : "r"(mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r"(mtvec_value)); + + // Setup PMP + uint32_t pmp_addr = ((uint32_t) (&flag)) >> 2; // Protect flag + asm volatile("csrw pmpaddr0, %0" : : "r"(pmp_addr)); + pmp_addr = 0x20001FFF; // Allows for the entire text, bss, stack section + asm volatile("csrw pmpaddr1, %0" : : "r"(pmp_addr)); + uint32_t pmp_cfg = 0x00001F11; // [NAPOT, RWX, no L] [NA4, R, no L] + asm volatile("csrw pmpcfg0, %0" : : "r"(pmp_cfg)); + + flag = 3; + + // Test registers in M_MODE + uint32_t csr_val_0, csr_val_1; + csr_val_0 = 0x0; + asm volatile("csrw mcycle, %0" : : "r"(csr_val_0)); + asm volatile("csrr %0, mcycle" : "=r"(csr_val_0)); + asm volatile("nop; nop; nop;"); + asm volatile("csrr %0, cycle" : "=r"(csr_val_1)); + + if (csr_val_1 - csr_val_0 < 15) // Are the two cycle counts close? + { + flag -= 1; + } + + asm volatile("csrw cycle, %0" : : "r"(csr_val_0)); // Trying to write to a R/O register + + + // Jump to user program + uint32_t mepc_value = (uint32_t) user_main; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + asm volatile("mret"); + + __builtin_unreachable(); + +} \ No newline at end of file diff --git a/verification/u-mode/link.ld b/verification/u-mode/link.ld new file mode 100755 index 000000000..4fd20ea69 --- /dev/null +++ b/verification/u-mode/link.ld @@ -0,0 +1,14 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x0000000080000000; + .text.init : { *(.text.init) } + .text : { *(.text) } + .data ALIGN(0x1000) : { *(.data) } + .bss : { *(.bss) } + . = 0x0000000080007000; + .statuses : { *(.statuses) } + _end = .; +} diff --git a/verification/u-mode/start.S b/verification/u-mode/start.S new file mode 100644 index 000000000..9e588dd07 --- /dev/null +++ b/verification/u-mode/start.S @@ -0,0 +1,17 @@ + +.global _start +_start: + li sp, 0x8000F000 + jal main + +.global done +done: + la x28, flag + lw x28, 0(x28) +__inf_loop: + j __inf_loop + + +.global flag +.data +flag: .word 0xFF diff --git a/verification/u-mode/u-mode.c b/verification/u-mode/u-mode.c new file mode 100644 index 000000000..f4f0d53dc --- /dev/null +++ b/verification/u-mode/u-mode.c @@ -0,0 +1,86 @@ +#include +#include "utility.h" + +extern volatile int flag; +extern volatile int done; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc, mtval; + mcause_t mcause; + asm volatile("csrr %0, mepc" : "=r"(mepc)); + asm volatile("csrr %0, mtval" : "=r"(mtval)); + asm volatile("csrr %0, mcause" : "=r"((mcause_t) mcause)); + + print("mepc: "); + put_uint32_hex(mepc); + print("\n"); + + print("mtval: "); + put_uint32_hex(mtval); + print("\n"); + + print("mcause: "); + put_uint32_hex(mcause.interrupt & 0x1); + print(" "); + put_uint32_hex(mcause.ex_code); + print("\n"); + + print("-----\n"); + + if (~mcause.interrupt && mcause.ex_code == 8){ + uint32_t mstatus = 0x1800; // set mpp back to M_MODE + asm volatile("csrs mstatus, %0" : : "r"(mstatus)); + mepc = (uint32_t) &done; + } + else + { + mepc += 4; + flag -= 1; + } + asm volatile("csrw mepc, %0" : : "r"(mepc)); +} + + +void __attribute__((noreturn)) user_main(void) { + print("A"); // MMIO region is not allowed in PMP, should fail + + flag = 0; // Flag is protected, should fail + + asm volatile("mret"); // privileged instruction + + uint32_t temp; + asm volatile("csrr %0, mstatus" : "=r"(temp)); // Machine mode CSR + + asm volatile("wfi"); // No timeout wait enabled + + asm volatile("ecall"); // ends user_main + + __builtin_unreachable(); + +} + +int main(void) { + // Setup exceptions + uint32_t mtvec_value = (uint32_t) handler; + uint32_t mstatus_value = 0x08; + asm volatile("csrs mstatus, %0" : : "r"(mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r"(mtvec_value)); + + // Setup PMP + uint32_t pmp_addr = ((uint32_t) (&flag)) >> 2; // Protect flag + asm volatile("csrw pmpaddr0, %0" : : "r"(pmp_addr)); + pmp_addr = 0x20001FFF; // Allows for the entire text, bss, stack section + asm volatile("csrw pmpaddr1, %0" : : "r"(pmp_addr)); + uint32_t pmp_cfg = 0x00001F11; // [NAPOT, RWX, no L] [NA4, R, no L] + asm volatile("csrw pmpcfg0, %0" : : "r"(pmp_cfg)); + + + // Jump to user program + flag = 6; + uint32_t mepc_value = (uint32_t) user_main; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + asm volatile("mret"); + + __builtin_unreachable(); + +} diff --git a/verification/u-mode/utility.c b/verification/u-mode/utility.c new file mode 100644 index 000000000..3a5488725 --- /dev/null +++ b/verification/u-mode/utility.c @@ -0,0 +1,25 @@ + +#include "utility.h" + +void print(char *string) { + volatile char *magic = (volatile char *)MAGIC_ADDR; + + for(int i = 0; string[i]; i++) { + (*magic) = string[i]; + } +} + +void put_uint32_hex(uint32_t x) { + char buf[10] = {0}; + + for(int i = 0; i < 8; i++) { + uint8_t value = (x & 0xF); + if(value >= 10) { + buf[7-i] = ((value-10) + 'A'); + } else { + buf[7-i] = (value + '0'); + } + x >>= 4; + } + print(buf); +} diff --git a/verification/u-mode/utility.h b/verification/u-mode/utility.h new file mode 100644 index 000000000..508607bdd --- /dev/null +++ b/verification/u-mode/utility.h @@ -0,0 +1,24 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + + #include + + #define MTIME_ADDR 0xFFFFFFE0 + #define MTIMEH_ADDR 0xFFFFFFE4 + #define MTIMECMP_ADDR 0xFFFFFFE8 + #define MTIMECMPH_ADDR 0xFFFFFFEC + #define MSIP_ADDR 0xFFFFFFF0 + #define EXT_ADDR_SET 0xFFFFFFF4 + #define EXT_ADDR_CLEAR 0xFFFFFFF8 + #define MAGIC_ADDR 0xFFFFFFFC + + + void print(char *string); + void put_uint32_hex(uint32_t hex); + + typedef struct { + int ex_code : 31; + int interrupt : 1; + } mcause_t; + +#endif From 00e898b1454a453d2f14e1cac3dcd16554bf74ea Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sat, 5 Nov 2022 21:12:11 -0400 Subject: [PATCH 19/47] tspp, standard_core: decouple rd selection from control unit For implementing pipelines with >2 stages, it is necessary for rd to be supplied by a later stage instead of the current instruction. This changes the control unit to output its own rd signal instead of feeding the register file directly. For tspp, the control unit's rd is assigned directly to the register file's rd, but for stage3 it will be passed to the next stage, and the next stage's rd will be fed to the register file for performing writeback. --- Makefile | 2 +- source_code/include/control_unit_if.vh | 3 ++- source_code/pipelines/tspp/tspp_execute_stage.sv | 1 + source_code/privs/priv_1_12/priv_1_12_pma.sv | 6 +++--- source_code/standard_core/control_unit.sv | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 25b883953..e64f1536e 100644 --- a/Makefile +++ b/Makefile @@ -57,7 +57,7 @@ config: @python3 scripts/config_core.py example.yml verilate: config - @fusesoc --cores-root . run --setup --build --build-root rvb_out --target sim --tool verilator socet:riscv:RISCVBusiness + @fusesoc --cores-root . run --setup --build --build-root rvb_out --target sim --tool verilator socet:riscv:RISCVBusiness --make_options='-j' @echo "------------------------------------------------------------------" @echo "Build finished, you can run with 'fusesoc run', or by navigating" @echo "to the build directory created by FuseSoC and using the Makefile there." diff --git a/source_code/include/control_unit_if.vh b/source_code/include/control_unit_if.vh index edb448b6e..d51f4b011 100644 --- a/source_code/include/control_unit_if.vh +++ b/source_code/include/control_unit_if.vh @@ -36,6 +36,7 @@ interface control_unit_if; logic [1:0] alu_a_sel, alu_b_sel; logic [2:0] w_sel; logic [4:0] shamt; + logic [4:0] rd; logic [11:0] imm_I, imm_S; logic [20:0] imm_UJ; logic [12:0] imm_SB; @@ -57,7 +58,7 @@ interface control_unit_if; imm_I, imm_S, imm_SB, imm_UJ, imm_U, imm_shamt_sel, alu_op, opcode, halt, wen, fault_insn, illegal_insn, ret_insn, breakpoint, ecall_insn, csr_swap, csr_set, csr_clr, csr_imm, csr_rw_valid, - csr_addr, zimm, ifence, wfi + csr_addr, zimm, ifence, wfi, rd ); endinterface diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index a33ee78de..7965ba730 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -206,6 +206,7 @@ module tspp_execute_stage ( end end + assign rf_if.rd = cu_if.rd; // Change to accommodate tspp and stage3 assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) & (~hazard_if.if_ex_stall | hazard_if.npc_sel | rv32cif.done_earlier) & ~(cu_if.dren & mal_addr); diff --git a/source_code/privs/priv_1_12/priv_1_12_pma.sv b/source_code/privs/priv_1_12/priv_1_12_pma.sv index 5dbd14a1f..e7a615514 100644 --- a/source_code/privs/priv_1_12/priv_1_12_pma.sv +++ b/source_code/privs/priv_1_12/priv_1_12_pma.sv @@ -84,13 +84,13 @@ module priv_1_12_pma ( new_val.pma_cfg_0.Rsrv = RsrvNone; end if (new_val.pma_cfg_1.Rsrv == RsrvReserved) begin - new_val.pma_cfg_0.Rsrv = RsrvNone; + new_val.pma_cfg_1.Rsrv = RsrvNone; end if (new_val.pma_cfg_0.AccWidth == AccWidthReserved) begin new_val.pma_cfg_0.AccWidth = WordAcc; end if (new_val.pma_cfg_1.AccWidth == AccWidthReserved) begin - new_val.pma_cfg_0.AccWidth = WordAcc; + new_val.pma_cfg_1.AccWidth = WordAcc; end nxt_pma_regs[priv_ext_if.csr_addr[3:0]] = new_val; @@ -131,4 +131,4 @@ module priv_1_12_pma ( end end -endmodule \ No newline at end of file +endmodule diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index ceb94a445..72814024a 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -58,7 +58,7 @@ module control_unit ( assign cu_if.opcode = opcode_t'(cu_if.instr[6:0]); assign rf_if.rs1 = rmgmt_req_reg_r ? rmgmt_rsel_s_0 : cu_if.instr[19:15]; assign rf_if.rs2 = rmgmt_req_reg_r ? rmgmt_rsel_s_1 : cu_if.instr[24:20]; - assign rf_if.rd = rmgmt_req_reg_w ? rmgmt_rsel_d : cu_if.instr[11:7]; + assign cu_if.rd = rmgmt_req_reg_w ? rmgmt_rsel_d : cu_if.instr[11:7]; assign cu_if.shamt = cu_if.instr[24:20]; // Assign the immediate values From fb466dab4041f81b8eafbd701cc62e1cddc9348a Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sat, 5 Nov 2022 21:18:27 -0400 Subject: [PATCH 20/47] Initial commit of 3-stage pipeline: All tests passing This adds a 3-stage pipeline, defined as follows: Fetch | Decode/Execute | Memory/Writeback There is a forwarding path from the second latch into the decode/execute stage. However, memory references cannot be forwarded due to critical path issues, so load->use and csr->use hazards will incur 1 extra cycle of stalling (beyond usual delay of load) for dependent instructions. Some rough estimates of frequency (standalone) are: - 70MHz max on FPGA - 200MHz+ feasible in ASIC This implies that 50MHz FPGA speed and 100MHz ASIC speed should be attainable, with more room to push the latter depending on the rest of the system. All the RV32I tests pass. Remaining TODOs are: - Alter the TB to allow testing of forwarding. Currently, memory references take so long that there are never back-to-back instructions in the pipeline. While this is somewhat realistic (AFT will have these delays), the addition of a cache (and prefetching effect of RV32C) will allow single-cycle instruction hits. - Implement exceptions/interrupts. This is currently untested. - Allow RV32C. Theoretically, this shouldn't cause any problems, since the parts of the pipeline that touch the decompressor were not altered. - RV32M. There needs to be a decision about whether RISC-MGMT should be supported for this pipeline. Ideally it should be added, but this is also a chance to remove the standard extensions from RISC-MGMT, and change it to be only for custom instructions if that is desired. --- .gitignore | 3 + RISCVBusiness.core | 7 +- dev.yml | 3 +- .../stage3/include/stage3_fetch_execute_if.vh | 44 ++ .../include/stage3_forwarding_unit_if.vh | 32 ++ .../stage3/include/stage3_hazard_unit_if.vh | 98 ++++ .../stage3/include/stage3_mem_pipe_if.vh | 33 ++ source_code/pipelines/stage3/source/stage3.sv | 64 +++ .../stage3/source/stage3_execute_stage.sv | 523 ++++++++++++++++++ .../stage3/source/stage3_fetch_stage.sv | 148 +++++ .../stage3/source/stage3_forwarding_unit.sv | 14 + .../stage3/source/stage3_hazard_unit.sv | 159 ++++++ .../stage3/source/stage3_mem_stage.sv | 238 ++++++++ .../stage3/source/stage3_types_pkg.sv | 54 ++ source_code/pipelines/stage3/stage3.core | 36 ++ source_code/standard_core/RISCVBusiness.sv | 9 +- source_code/standard_core/top_core.sv | 34 +- wave_format.gtkw | 141 +++++ 18 files changed, 1619 insertions(+), 21 deletions(-) create mode 100644 source_code/pipelines/stage3/include/stage3_fetch_execute_if.vh create mode 100644 source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh create mode 100644 source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh create mode 100644 source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh create mode 100644 source_code/pipelines/stage3/source/stage3.sv create mode 100644 source_code/pipelines/stage3/source/stage3_execute_stage.sv create mode 100644 source_code/pipelines/stage3/source/stage3_fetch_stage.sv create mode 100644 source_code/pipelines/stage3/source/stage3_forwarding_unit.sv create mode 100644 source_code/pipelines/stage3/source/stage3_hazard_unit.sv create mode 100644 source_code/pipelines/stage3/source/stage3_mem_stage.sv create mode 100644 source_code/pipelines/stage3/source/stage3_types_pkg.sv create mode 100644 source_code/pipelines/stage3/stage3.core create mode 100644 wave_format.gtkw diff --git a/.gitignore b/.gitignore index ac457f2e4..dab1eb6d5 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,6 @@ open_files.sh # fusesoc fusesoc_libraries fusesoc.conf + +# synthesis scripts +desflow/** diff --git a/RISCVBusiness.core b/RISCVBusiness.core index b2309cb4d..3fa4c880d 100644 --- a/RISCVBusiness.core +++ b/RISCVBusiness.core @@ -9,6 +9,7 @@ filesets: - "socet:bus-components:ahb_if" - "socet:bus-components:apb_if" - "socet:riscv:packages" + - "socet:riscv:stage3" - "socet:riscv:priv" - "socet:riscv:caches" - "socet:riscv:risc_mgmt" @@ -19,9 +20,9 @@ filesets: - source_code/branch_predictors/branch_predictor_wrapper.sv - source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv - source_code/bus_bridges/generic_nonpipeline.sv - - source_code/pipelines/tspp/tspp_hazard_unit.sv - - source_code/pipelines/tspp/tspp_fetch_stage.sv - - source_code/pipelines/tspp/tspp_execute_stage.sv + #- source_code/pipelines/tspp/tspp_hazard_unit.sv + #- source_code/pipelines/tspp/tspp_fetch_stage.sv + #- source_code/pipelines/tspp/tspp_execute_stage.sv - source_code/sparce/sparce_disabled/sparce_disabled.sv - source_code/RV32E/rv32e_reg_file.sv - source_code/RV32E/rv32e_wrapper.sv diff --git a/dev.yml b/dev.yml index e73c60907..8d153ea28 100644 --- a/dev.yml +++ b/dev.yml @@ -20,7 +20,7 @@ microarch_params: sparce_enabled : "disabled" # RV32C - rv32c_enabled : "enabled" + rv32c_enabled : "disabled" # Halt infinite_loop_halts : "true" @@ -31,6 +31,5 @@ microarch_params: # RISC-MGMT Extension Configuration risc_mgmt_params: standard_extensions: - - name: "rv32m" nonstandard_extensions: diff --git a/source_code/pipelines/stage3/include/stage3_fetch_execute_if.vh b/source_code/pipelines/stage3/include/stage3_fetch_execute_if.vh new file mode 100644 index 000000000..879dd70f0 --- /dev/null +++ b/source_code/pipelines/stage3/include/stage3_fetch_execute_if.vh @@ -0,0 +1,44 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_fetch_execute_if.vh +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/01/2016 +* Description: Interface between the fetch and execute pipeline stages +*/ + +`ifndef STAGE3_FETCH_EXECUTE_IF_VH +`define STAGE3_FETCH_EXECUTE_IF_VH + +interface stage3_fetch_execute_if; + import rv32i_types_pkg::*; + import stage3_types_pkg::*; + + fetch_ex_t fetch_ex_reg; + word_t brj_addr; + + modport fetch( + output fetch_ex_reg + ); + + modport execute( + input fetch_ex_reg + ); + +endinterface +`endif diff --git a/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh b/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh new file mode 100644 index 000000000..d2b6b441d --- /dev/null +++ b/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh @@ -0,0 +1,32 @@ +`ifndef __STAGE3_FORWARD_UNIT_VH__ +`define __STAGE3_FORWARD_UNIT_VH__ + +interface stage3_forwarding_unit_if(); + + logic [4:0] rd_mem; + logic [4:0] rs1_e; + logic [4:0] rs2_e; + logic regWEN; + logic load; + logic fwd_rs1; + logic fwd_rs2; + rv32i_types_pkg::word_t rd_mem_data; + + modport execute( + input fwd_rs1, fwd_rs2, rd_mem_data, + output rs1_e, rs2_e + ); + + modport mem( + output rd_mem, rd_mem_data, regWEN, load + ); + + modport fw_unit( + input rs1_e, rs2_e, rd_mem, regWEN, load, + output fwd_rs1, fwd_rs2 + ); + +endinterface + + +`endif diff --git a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh new file mode 100644 index 000000000..6b88fb566 --- /dev/null +++ b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh @@ -0,0 +1,98 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_hazard_unit_if.vh +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/15/2016 +* Description: Interface for the hazard unit of the two stage pipeline +*/ + +`ifndef STAGE3_HAZARD_UNIT_IF_VH +`define STAGE3_HAZARD_UNIT_IF_VH + +interface stage3_hazard_unit_if(); + + import rv32i_types_pkg::word_t; + + // Pipeline status signals (inputs) + logic [4:0] rs1_e, rs2_e, rd_m; + logic reg_write, csr_read; + logic i_mem_busy, d_mem_busy, dren, dwen, ret; + logic jump, branch, fence_stall; + logic mispredict, halt; + word_t pc; + + // Control (outputs) + logic pc_en, npc_sel; + logic if_ex_flush, ex_mem_flush; + logic if_ex_stall, ex_mem_stall; + logic iren; + + // xTVEC Insertion + word_t priv_pc; + logic insert_priv_pc; + + //Pipeline Exceptions (inputs) + logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, + breakpoint, env_m; + word_t epc, badaddr; + + // Pipeline Tokens + logic token_ex; + logic token_mem; + + // RV32C + logic rv32c_ready; + + modport hazard_unit ( + input rs1_e, rs2_e, rd_m, + reg_write, csr_read, + i_mem_busy, d_mem_busy, dren, dwen, ret, + jump, branch, fence_stall, mispredict, halt, pc, + fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, + epc, badaddr, + token_ex, token_mem, rv32c_ready, + output pc_en, npc_sel, + if_ex_flush, ex_mem_flush, + if_ex_stall, ex_mem_stall, + priv_pc, insert_priv_pc, iren + ); + + modport fetch ( + input pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren, + output i_mem_busy, rv32c_ready + ); + + modport execute ( + input ex_mem_stall, ex_mem_flush, npc_sel, + output rs1_e, rs2_e, token_ex + ); + + modport mem ( + input ex_mem_stall, ex_mem_flush, + output rd_m, reg_write, csr_read, + d_mem_busy, dren, dwen, ret, + jump, branch, fence_stall, mispredict, halt, pc, + fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, + epc, badaddr, + token_mem + ); + + endinterface + +`endif diff --git a/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh b/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh new file mode 100644 index 000000000..55669b87e --- /dev/null +++ b/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh @@ -0,0 +1,33 @@ +`ifndef __STAGE3_MEM_PIPE_IF__ +`define __STAGE3_MEM_PIPE_IF__ + +interface stage3_mem_pipe_if(); + + import rv32i_types_pkg::*; + import stage3_types_pkg::*; + + logic reg_write; + logic [4:0] rd_m; + ex_mem_t ex_mem_reg; + word_t brj_addr; + word_t reg_wdata; + + modport fetch( + input brj_addr + ); + + + modport execute( + input reg_wdata, reg_write, rd_m, + output ex_mem_reg + ); + + modport mem( + input ex_mem_reg, + output brj_addr, reg_wdata, reg_write, rd_m + ); + +endinterface + + +`endif diff --git a/source_code/pipelines/stage3/source/stage3.sv b/source_code/pipelines/stage3/source/stage3.sv new file mode 100644 index 000000000..703f10452 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3.sv @@ -0,0 +1,64 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Two Stage In-Order Pipeline +*/ + +`include "stage3_fetch_execute_if.vh" +`include "stage3_hazard_unit_if.vh" +`include "predictor_pipeline_if.vh" +`include "generic_bus_if.vh" +`include "prv_pipeline_if.vh" +`include "risc_mgmt_if.vh" +`include "cache_control_if.vh" +`include "sparce_pipeline_if.vh" +`include "rv32c_if.vh" + +module stage3 #( + RESET_PC = 32'h80000000 +)( + input CLK, + input nRST, + output logic halt, + output logic wfi, + generic_bus_if.cpu igen_bus_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if prv_pipe_if, + predictor_pipeline_if predict_if, + risc_mgmt_if rm_if, + cache_control_if cc_if, + sparce_pipeline_if sparce_if, + rv32c_if rv32cif +); + //interface instantiations + stage3_fetch_execute_if fetch_ex_if(); + stage3_mem_pipe_if mem_pipe_if(); + stage3_hazard_unit_if hazard_if(); + stage3_forwarding_unit_if fw_if(); + + //module instantiations + stage3_fetch_stage #(.RESET_PC(RESET_PC)) fetch_stage_i(.mem_fetch_if(mem_pipe_if), .*); + stage3_execute_stage execute_stage_i(.ex_mem_if(mem_pipe_if), .*); + stage3_mem_stage mem_stage_i(.ex_mem_if(mem_pipe_if), .*); + stage3_hazard_unit hazard_unit_i(.*); + stage3_forwarding_unit forward_unit_i(.*); + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv new file mode 100644 index 000000000..c71c9e419 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -0,0 +1,523 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_execute_stage.sv +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/16/2016 +* Description: Execute Stage for the Two Stage Pipeline +*/ + +`include "stage3_fetch_execute_if.vh" +`include "stage3_hazard_unit_if.vh" +`include "stage3_forwarding_unit_if.vh" +`include "control_unit_if.vh" +`include "component_selection_defines.vh" +`include "rv32i_reg_file_if.vh" +`include "alu_if.vh" +//`include "prv_pipeline_if.vh" +`include "risc_mgmt_if.vh" +`include "rv32c_if.vh" + +module stage3_execute_stage ( + input CLK, + input nRST, + stage3_fetch_execute_if.execute fetch_ex_if, + stage3_mem_pipe_if.execute ex_mem_if, + stage3_hazard_unit_if.execute hazard_if, + stage3_forwarding_unit_if.execute fw_if, + //prv_pipeline_if.pipe prv_pipe_if, + risc_mgmt_if.ts_execute rm_if, + sparce_pipeline_if.pipe_execute sparce_if, + rv32c_if.execute rv32cif +); + + import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; + import stage3_types_pkg::*; + + // Interface declarations + control_unit_if cu_if (); + rv32i_reg_file_if rf_if (); + alu_if alu_if (); + jump_calc_if jump_if (); + branch_res_if branch_if (); + + // Module instantiations + control_unit cu ( + .cu_if(cu_if), + .rf_if(rf_if), + .rmgmt_rsel_s_0(rm_if.rsel_s_0), + .rmgmt_rsel_s_1(rm_if.rsel_s_1), + .rmgmt_rsel_d(rm_if.rsel_d), + .rmgmt_req_reg_r(rm_if.req_reg_r), + .rmgmt_req_reg_w(rm_if.req_reg_w) + ); + + assign wfi = cu_if.wfi; //Added by rkannank + + generate + if (BASE_ISA == "RV32E") begin : g_rfile_select + rv32e_reg_file rf ( + .CLK, + .nRST, + .rf_if + ); + end else begin : g_rfile_select + rv32i_reg_file rf ( + .CLK, + .nRST, + .rf_if + ); + end + endgenerate + + // Forwarding -- replace rs1/rs2 with post-forwarded versions + word_t rs1_post_fwd, rs2_post_fwd; + // TODO: Make sure rs2_post_fwd used everywhere rs2 data is meant to be used! + assign rs1_post_fwd = fw_if.fwd_rs1 ? fw_if.rd_mem_data : rf_if.rs1_data; + assign rs2_post_fwd = fw_if.fwd_rs2 ? fw_if.rd_mem_data : rf_if.rs2_data; + + alu alu (.*); + jump_calc jump_calc (.*); + + branch_res branch_res (.br_if(branch_if)); + + /* + word_t store_swapped; + endian_swapper store_swap ( + .word_in (rf_if.rs2_data), + .word_out(store_swapped) + ); + + word_t dload_ext; + logic [3:0] byte_en, byte_en_temp, byte_en_standard; + dmem_extender dmem_ext ( + .dmem_in (dgen_bus_if.rdata), + .load_type(cu_if.load_type), + .byte_en (byte_en), + .ext_out (dload_ext) + ); + */ + + /******************************************************* + * MISC RISC-MGMT Logic + *******************************************************/ + + //assign rm_if.rdata_s_0 = rf_if.rs1_data; + //assign rm_if.rdata_s_1 = rf_if.rs2_data; + + + /******************************************************** + *** Choose the Endianness Coming into the processor + *******************************************************/ + /* + generate + if (BUS_ENDIANNESS == "big") begin : g_data_bus_be + assign byte_en = byte_en_temp; + end else if (BUS_ENDIANNESS == "little") begin : g_data_bus_le + assign byte_en = cu_if.dren ? byte_en_temp : + {byte_en_temp[0], byte_en_temp[1], + byte_en_temp[2], byte_en_temp[3]}; + end + endgenerate + */ + + //RV32C + assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; + assign rv32cif.halt = cu_if.halt; + assign rv32cif.ex_busy = cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; + assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + + + /******************************************************* + *** Sign Extensions + *******************************************************/ + word_t imm_I_ext, imm_S_ext, imm_UJ_ext; + assign imm_I_ext = {{20{cu_if.imm_I[11]}}, cu_if.imm_I}; + assign imm_UJ_ext = {{11{cu_if.imm_UJ[20]}}, cu_if.imm_UJ}; + assign imm_S_ext = {{20{cu_if.imm_S[11]}}, cu_if.imm_S}; + + /******************************************************* + *** Jump Target Calculator and Associated Logic + *******************************************************/ + word_t jump_addr /* verilator isolate_assignments */; + always_comb begin + if (cu_if.j_sel) begin + jump_if.base = fetch_ex_if.fetch_ex_reg.pc; + jump_if.offset = imm_UJ_ext; + jump_addr = jump_if.jal_addr; + end else begin + jump_if.base = rs1_post_fwd; + jump_if.offset = imm_I_ext; + jump_addr = jump_if.jalr_addr; + end + end + + /******************************************************* + *** ALU and Associated Logic + *******************************************************/ + word_t imm_or_shamt; + assign imm_or_shamt = (cu_if.imm_shamt_sel == 1'b1) ? cu_if.shamt : imm_I_ext; + assign alu_if.aluop = cu_if.alu_op; + logic mal_addr; + + always_comb begin + case (cu_if.alu_a_sel) + 2'd0: alu_if.port_a = rs1_post_fwd; + 2'd1: alu_if.port_a = imm_S_ext; + 2'd2: alu_if.port_a = fetch_ex_if.fetch_ex_reg.pc; + 2'd3: alu_if.port_a = '0; //Not Used + endcase + end + + always_comb begin + case (cu_if.alu_b_sel) + 2'd0: alu_if.port_b = rs1_post_fwd; + 2'd1: alu_if.port_b = rs2_post_fwd; + 2'd2: alu_if.port_b = imm_or_shamt; + 2'd3: alu_if.port_b = cu_if.imm_U; + endcase + end + + // TODO: Fix this for correct writeback! Mem stage needs mux this for us + assign rf_if.w_data = ex_mem_if.reg_wdata; + assign rf_if.rd = ex_mem_if.rd_m; + /* + always_comb begin + if (rm_if.req_reg_w) begin + rf_if.w_data = rm_if.reg_wdata; + end else begin + + case (cu_if.w_sel) + 3'd0: rf_if.w_data = dload_ext; + 3'd1: rf_if.w_data = fetch_ex_if.fetch_ex_reg.pc4; + 3'd2: rf_if.w_data = cu_if.imm_U; + 3'd3: rf_if.w_data = alu_if.port_out; + 3'd4: rf_if.w_data = prv_pipe_if.rdata; + default: rf_if.w_data = '0; + endcase + end + end + */ + + // TODO: What is the real condition for writeback? + // Does checking the stall actually matter? (value is coming from register, no path from register update->register input now) + /*assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) + & (~hazard_if.ex_mem_stall | hazard_if.npc_sel | rv32cif.done_earlier) + & ~(cu_if.dren & mal_addr); + */ + assign rf_if.wen = ex_mem_if.reg_write && !hazard_if.ex_mem_stall; // TODO: The second signal only matters for some miniscule power reduction by not writing each cycle. This is correct with only the wen signal due to no loop from reg read to reg write + /******************************************************* + *** Branch Target Resolution and Associated Logic + *******************************************************/ + + word_t resolved_addr; + logic branch_taken; + word_t branch_addr; + word_t brj_addr; + + assign branch_if.rs1_data = rs1_post_fwd;//rf_if.rs1_data; + assign branch_if.rs2_data = rs2_post_fwd; //rf_if.rs2_data; + assign branch_if.pc = fetch_ex_if.fetch_ex_reg.pc; + assign branch_if.imm_sb = cu_if.imm_SB; + assign branch_if.branch_type = cu_if.branch_type; + + // Mux resource based on if RISC-MGMT is trying to access it + assign branch_taken = rm_if.req_br_j ? rm_if.branch_jump : branch_if.branch_taken; + assign branch_addr = rm_if.req_br_j ? rm_if.br_j_addr : branch_if.branch_addr; + //assign rm_if.pc = fetch_ex_if.fetch_ex_reg.pc; + + assign resolved_addr = branch_if.branch_taken ? branch_addr : fetch_ex_if.fetch_ex_reg.pc4; + + assign brj_addr = ((cu_if.ex_pc_sel == 1'b1) && ~rm_if.req_br_j) ? + jump_addr : resolved_addr; + + //assign hazard_if.mispredict = fetch_ex_if.fetch_ex_reg.prediction ^ branch_taken; + + /******************************** + * Hazard/Forwarding Unit Signals + *********************************/ + assign hazard_if.rs1_e = rf_if.rs1; + assign hazard_if.rs2_e = rf_if.rs2; + + assign fw_if.rs1_e = rf_if.rs1; + assign fw_if.rs2_e = rf_if.rs2; + + + // TODO: NEW + always_ff @(posedge CLK, negedge nRST) begin + if(!nRST) begin + ex_mem_if.ex_mem_reg <= '{default: '0}; + end else begin + // TODO: This register is ~180b. Not awful, but can it be smaller? + // PS: Does it even matter? Synth. tools may be able to merge regs. + if(!hazard_if.ex_mem_flush && !hazard_if.ex_mem_stall) begin + // TODO: Handle case of exceptions earlier in the pipe being passed on to handle in the last stage + // Single bit control signals + ex_mem_if.ex_mem_reg.branch <= cu_if.branch; + ex_mem_if.ex_mem_reg.prediction <= fetch_ex_if.fetch_ex_reg.prediction; + ex_mem_if.ex_mem_reg.branch_taken <= branch_if.branch_taken; + ex_mem_if.ex_mem_reg.dren <= cu_if.dren; + ex_mem_if.ex_mem_reg.dwen <= cu_if.dwen; + ex_mem_if.ex_mem_reg.reg_write <= cu_if.wen; + ex_mem_if.ex_mem_reg.ifence <= cu_if.ifence; + ex_mem_if.ex_mem_reg.jump <= cu_if.jump; + ex_mem_if.ex_mem_reg.halt <= cu_if.halt; + ex_mem_if.ex_mem_reg.csr_swap <= cu_if.csr_swap; + ex_mem_if.ex_mem_reg.csr_clr <= cu_if.csr_clr; + ex_mem_if.ex_mem_reg.csr_set <= cu_if.csr_set; + ex_mem_if.ex_mem_reg.csr_imm <= cu_if.csr_imm; + ex_mem_if.ex_mem_reg.breakpoint <= cu_if.breakpoint; + ex_mem_if.ex_mem_reg.ecall_insn <= cu_if.ecall_insn; + ex_mem_if.ex_mem_reg.ret_insn <= cu_if.ret_insn; + ex_mem_if.ex_mem_reg.was_compressed <= 1'b0; // TODO: RV32C support + + // Bit vectors + ex_mem_if.ex_mem_reg.w_sel <= cu_if.w_sel; + ex_mem_if.ex_mem_reg.zimm <= cu_if.zimm; + ex_mem_if.ex_mem_reg.rd_m <= cu_if.rd; + ex_mem_if.ex_mem_reg.load_type <= cu_if.load_type; + ex_mem_if.ex_mem_reg.csr_addr <= cu_if.csr_addr; + + // Word sized members + ex_mem_if.ex_mem_reg.brj_addr <= brj_addr; + ex_mem_if.ex_mem_reg.port_out <= alu_if.port_out; + ex_mem_if.ex_mem_reg.rs1_data <= rs1_post_fwd; + ex_mem_if.ex_mem_reg.rs2_data <= rs2_post_fwd; + ex_mem_if.ex_mem_reg.instr <= cu_if.instr; + ex_mem_if.ex_mem_reg.pc <= fetch_ex_if.fetch_ex_reg.pc; + ex_mem_if.ex_mem_reg.pc4 <= fetch_ex_if.fetch_ex_reg.pc4; + ex_mem_if.ex_mem_reg.imm_U <= cu_if.imm_U; + + end else if(hazard_if.ex_mem_flush && !hazard_if.ex_mem_stall) begin + ex_mem_if.ex_mem_reg <= '{default: '0}; + end + // else: retain state + end + end + + //// + // HERE BELOW WAS MOVED TO MEMORY STAGE + //// + + /******************************************************* + *** Data Ram Interface Logic + *******************************************************/ + /* + logic [1:0] byte_offset; + + // RISC-MGMT connection + assign rm_if.mem_load = dgen_bus_if.rdata; + + assign dgen_bus_if.ren = rm_if.req_mem ? rm_if.mem_ren : cu_if.dren & ~mal_addr & ~prv_pipe_if.prot_fault_l; + assign dgen_bus_if.wen = rm_if.req_mem ? rm_if.mem_wen : cu_if.dwen & ~mal_addr & ~prv_pipe_if.prot_fault_s; + assign byte_en_temp = rm_if.req_mem ? rm_if.mem_byte_en : byte_en_standard; + assign dgen_bus_if.byte_en = byte_en; + assign dgen_bus_if.addr = rm_if.req_mem ? rm_if.mem_addr : alu_if.port_out; + assign hazard_if.d_mem_busy = dgen_bus_if.busy; + assign byte_offset = alu_if.port_out[1:0]; + + always_comb begin + dgen_bus_if.wdata = '0; + if (rm_if.req_mem) dgen_bus_if.wdata = rm_if.mem_store; + else begin + case (cu_if.load_type) // load_type can be used for store_type as well + LB: dgen_bus_if.wdata = {4{rf_if.rs2_data[7:0]}}; + LH: dgen_bus_if.wdata = {2{rf_if.rs2_data[15:0]}}; + LW: dgen_bus_if.wdata = rf_if.rs2_data; + endcase + end + end + + + // Assign byte_en based on load type + // funct3 for loads and stores are the same bit positions + // byte_en is valid for both loads and stores + always_comb begin + unique case (cu_if.load_type) + LB: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0001; + 2'b01: byte_en_standard = 4'b0010; + 2'b10: byte_en_standard = 4'b0100; + 2'b11: byte_en_standard = 4'b1000; + default: byte_en_standard = 4'b0000; + endcase + end + LBU: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0001; + 2'b01: byte_en_standard = 4'b0010; + 2'b10: byte_en_standard = 4'b0100; + 2'b11: byte_en_standard = 4'b1000; + default: byte_en_standard = 4'b0000; + endcase + end + LH: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0011; + 2'b10: byte_en_standard = 4'b1100; + default: byte_en_standard = 4'b0000; + endcase + end + LHU: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0011; + 2'b10: byte_en_standard = 4'b1100; + default: byte_en_standard = 4'b0000; + endcase + end + LW: byte_en_standard = 4'b1111; + default: byte_en_standard = 4'b0000; + endcase + end + */ + + // Fence instructions + + // posedge detector for ifence + // subsequent ifences will have same effect as a single fence + /* + logic ifence_reg; + logic ifence_pulse; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) ifence_reg <= 1'b0; + else ifence_reg <= cu_if.ifence; + end + + assign ifence_pulse = cu_if.ifence && ~ifence_reg; + assign cc_if.icache_flush = ifence_pulse; + assign cc_if.icache_clear = 1'b0; + assign cc_if.dcache_flush = ifence_pulse; + assign cc_if.dcache_clear = 1'b0; + + //regs to detect flush completion + logic dflushed, iflushed; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) iflushed <= 1'b1; + else if (ifence_pulse) iflushed <= 1'b0; + else if (cc_if.iflush_done) iflushed <= 1'b1; + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) dflushed <= 1'b1; + else if (ifence_pulse) dflushed <= 1'b0; + else if (cc_if.dflush_done) dflushed <= 1'b1; + end + + assign hazard_if.fence_stall = cu_if.ifence && (~dflushed || ~iflushed); + */ + + /******************************************************* + *** Hazard Unit Interface Logic + *******************************************************/ + /*assign hazard_if.dren = cu_if.dren; + assign hazard_if.dwen = cu_if.dwen; + assign hazard_if.jump = cu_if.jump; + assign hazard_if.branch = cu_if.branch; + assign hazard_if.halt = halt; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) halt <= 1'b0; + else if (cu_if.halt) halt <= cu_if.halt; + end*/ + + /******************************************************* + *** CSR / Priv Interface Logic + *******************************************************/ + /* + assign prv_pipe_if.swap = cu_if.csr_swap; + assign prv_pipe_if.clr = cu_if.csr_clr; + assign prv_pipe_if.set = cu_if.csr_set; + assign prv_pipe_if.wdata = cu_if.csr_imm ? {27'h0, cu_if.zimm} : rf_if.rs1_data; + assign prv_pipe_if.csr_addr = cu_if.csr_addr; + assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr | + prv_pipe_if.set) & ~hazard_if.if_ex_stall; + assign prv_pipe_if.instr = (cu_if.instr != '0); + + + + + + //Send exceptions to Hazard Unit + assign hazard_if.fault_l = 1'b0; + assign hazard_if.mal_l = cu_if.dren & mal_addr; + assign hazard_if.fault_s = 1'b0; + assign hazard_if.mal_s = cu_if.dwen & mal_addr; + assign hazard_if.breakpoint = cu_if.breakpoint; + assign hazard_if.env_m = cu_if.ecall_insn; + assign hazard_if.ret = cu_if.ret_insn; + assign hazard_if.badaddr_e = dgen_bus_if.addr; + + */ + // TODO: Illegal instruction checks. Maybe pass these on to next stage? + //assign hazard_if.illegal_insn = (cu_if.illegal_insn & ~rm_if.ex_token) + // | prv_pipe_if.invalid_csr; + //assign hazard_if.illegal_insn = 1'b0; + + //assign hazard_if.epc_e = fetch_ex_if.fetch_ex_reg.pc; + //assign hazard_if.token_ex = fetch_ex_if.fetch_ex_reg.token; + + // setup signals for memory protection + // not considering risc-mgmt here TODO + /* + assign prv_pipe_if.dren = cu_if.dren & ~mal_addr; + assign prv_pipe_if.dwen = cu_if.dwen & ~mal_addr; + assign prv_pipe_if.daddr = alu_if.port_out; + assign prv_pipe_if.d_acc_width = WordAcc; + */ + + /********************************************************* + *** Branch Predictor Logic + *********************************************************/ + /* + assign predict_if.update_predictor = cu_if.branch; + assign predict_if.prediction = fetch_ex_if.fetch_ex_reg.prediction; + assign predict_if.branch_result = branch_if.branch_taken; + //predict_if.update_addr = ; + */ + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + assign sparce_if.wb_data = rf_if.w_data; + assign sparce_if.wb_en = rf_if.wen; + assign sparce_if.sasa_data = rf_if.rs2_data; + assign sparce_if.sasa_addr = alu_if.port_out; + assign sparce_if.sasa_wen = cu_if.dwen; + assign sparce_if.rd = rf_if.rd; + + /********************************************************* + *** Signals for Bind Tracking - Read-Only, These don't affect execution + *********************************************************/ + /* + logic wb_stall; + logic [2:0] funct3; + logic [11:0] funct12; + logic instr_30; + + assign wb_stall = hazard_if.if_ex_stall & ~hazard_if.jump & ~hazard_if.branch; + assign funct3 = cu_if.instr[14:12]; + assign funct12 = cu_if.instr[31:20]; + assign instr_30 = cu_if.instr[30]; + */ + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv new file mode 100644 index 000000000..0f723efe3 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv @@ -0,0 +1,148 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_fetch_stage.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/19/2016 +* Description: Fetch stage for the two stage pipeline +*/ + +`include "stage3_fetch_execute_if.vh" +`include "stage3_hazard_unit_if.vh" +`include "predictor_pipeline_if.vh" +`include "generic_bus_if.vh" +`include "component_selection_defines.vh" +`include "cache_control_if.vh" +`include "rv32c_if.vh" +`include "prv_pipeline_if.vh" + +module stage3_fetch_stage ( + input logic CLK, + nRST, + stage3_fetch_execute_if.fetch fetch_ex_if, + stage3_mem_pipe_if.fetch mem_fetch_if, + stage3_hazard_unit_if.fetch hazard_if, + predictor_pipeline_if.access predict_if, + generic_bus_if.cpu igen_bus_if, + sparce_pipeline_if.pipe_fetch sparce_if, + rv32c_if.fetch rv32cif, + prv_pipeline_if.fetch prv_pipe_if +); + import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; + + parameter logic [31:0] RESET_PC = 32'h80000000; + + word_t pc, pc4or2, npc, instr; + + //PC logic + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + pc <= RESET_PC; + end else if (hazard_if.pc_en | rv32cif.done_earlier) begin + pc <= npc; + end + end + + //RV32C + assign rv32cif.inst = igen_bus_if.rdata; + assign rv32cif.inst_arrived = hazard_if.if_ex_flush == 0 & hazard_if.if_ex_stall == 0; + assign rv32cif.reset_en = hazard_if.insert_priv_pc | sparce_if.skipping + | hazard_if.npc_sel | predict_if.predict_taken; + assign rv32cif.pc_update = hazard_if.pc_en; + assign rv32cif.reset_pc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (sparce_if.skipping ? sparce_if.sparce_target + : (hazard_if.npc_sel ? mem_fetch_if.brj_addr + : (predict_if.predict_taken ? predict_if.target_addr + : pc4or2))); + assign rv32cif.reset_pc_val = RESET_PC; + + assign pc4or2 = (rv32cif.rv32c_ena & (rv32cif.result[1:0] != 2'b11)) ? (pc + 2) : (pc + 4); + assign predict_if.current_pc = pc; + assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (sparce_if.skipping ? sparce_if.sparce_target + : (hazard_if.npc_sel ? mem_fetch_if.brj_addr + : (predict_if.predict_taken ? predict_if.target_addr + : rv32cif.rv32c_ena ? rv32cif.nextpc + : pc4or2))); + + //Instruction Access logic + assign hazard_if.i_mem_busy = igen_bus_if.busy; + assign igen_bus_if.addr = rv32cif.rv32c_ena ? rv32cif.imem_pc : pc; + assign igen_bus_if.ren = hazard_if.iren & !rv32cif.done_earlier; + assign igen_bus_if.wen = 1'b0; + assign igen_bus_if.byte_en = 4'b1111; + assign igen_bus_if.wdata = '0; + + //Send exceptions through pipeline + logic mal_addr; + logic fault_insn; + logic mal_insn; + word_t badaddr; + + assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); + assign fault_insn = 1'b0; + assign mal_insn = mal_addr; + assign badaddr_f = igen_bus_if.addr; + + + //Fetch Execute Pipeline Signals + word_t instr_to_ex; + assign instr_to_ex = rv32cif.rv32c_ena ? rv32cif.result : igen_bus_if.rdata; + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) fetch_ex_if.fetch_ex_reg <= '0; + else if (hazard_if.if_ex_flush && !hazard_if.if_ex_stall) fetch_ex_if.fetch_ex_reg <= '0; + else if (((rv32cif.done | rv32cif.done_earlier) & rv32cif.rv32c_ena) + | (!hazard_if.if_ex_stall & !rv32cif.rv32c_ena)) begin + fetch_ex_if.fetch_ex_reg.token <= 1'b1; + fetch_ex_if.fetch_ex_reg.mal_insn <= mal_insn; + fetch_ex_if.fetch_ex_reg.fault_insn <= fault_insn; + fetch_ex_if.fetch_ex_reg.badaddr <= badaddr; + fetch_ex_if.fetch_ex_reg.pc <= pc; + fetch_ex_if.fetch_ex_reg.pc4 <= pc4or2; + fetch_ex_if.fetch_ex_reg.instr <= instr_to_ex; + fetch_ex_if.fetch_ex_reg.prediction <= predict_if.predict_taken; // TODO: This is just wrong... + end + end + + + // Send memory protection signals + assign prv_pipe_if.iren = igen_bus_if.ren; + assign prv_pipe_if.iaddr = igen_bus_if.addr; + assign prv_pipe_if.i_acc_width = WordAcc; + + // Choose the endianness of the data coming into the processor + generate + if (BUS_ENDIANNESS == "big") assign instr = igen_bus_if.rdata; + else if (BUS_ENDIANNESS == "little") + endian_swapper ltb_endian ( + .word_in(igen_bus_if.rdata), + .word_out(instr) + ); + endgenerate + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + + assign sparce_if.pc = pc; + assign sparce_if.rdata = igen_bus_if.rdata; +endmodule + + diff --git a/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv b/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv new file mode 100644 index 000000000..9f6e20829 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv @@ -0,0 +1,14 @@ + +module stage3_forwarding_unit( + stage3_forwarding_unit_if.fw_unit fw_if +); + + logic rs1_match, rs2_match; + + assign rs1_match = (fw_if.rd_mem != 0) && (fw_if.rs1_e == fw_if.rd_mem); + assign rs2_match = (fw_if.rd_mem != 0) && (fw_if.rs2_e == fw_if.rd_mem); + + assign fw_if.fwd_rs1 = rs1_match && fw_if.regWEN && !fw_if.load; + assign fw_if.fwd_rs2 = rs2_match && fw_if.regWEN && !fw_if.load; + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv new file mode 100644 index 000000000..df8c6b39e --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -0,0 +1,159 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_hazard_unit.sv +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/14/2016 +* Description: Hazard unit that controls the flushing and stalling of +* the stages of the Two Stage Pipeline +*/ + +`include "stage3_hazard_unit_if.vh" +`include "prv_pipeline_if.vh" +`include "risc_mgmt_if.vh" + +module stage3_hazard_unit ( + stage3_hazard_unit_if.hazard_unit hazard_if, + prv_pipeline_if.hazard prv_pipe_if, + risc_mgmt_if.ts_hazard rm_if, + sparce_pipeline_if.hazard sparce_if +); + import alu_types_pkg::*; + import rv32i_types_pkg::*; + + // Pipeline hazard signals + logic dmem_access; + logic branch_jump; + logic wait_for_imem; + logic wait_for_dmem; + logic rs1_match; + logic rs2_match; + logic mem_use_stall; + logic cannot_forward; + logic fetch_busy; + logic execute_busy; + logic mem_busy; + + // IRQ/Exception hazard signals + logic ex_flush_hazard; + logic exception; + logic intr; + + // TODO: RISC-MGMT + logic rmgmt_stall; + + assign rm_if.if_ex_enable = ~hazard_if.if_ex_stall; + assign rmgmt_stall = rm_if.memory_stall | rm_if.execute_stall; + + // Hazard detection + assign rs1_match = (hazard_if.rs1_e == hazard_if.rd_m) && (hazard_if.rd_m != 0); + assign rs2_match = (hazard_if.rs2_e == hazard_if.rd_m) && (hazard_if.rd_m != 0); + assign cannot_forward = (hazard_if.dren || hazard_if.csr_read); // cannot forward outputs generated in mem stage + + assign dmem_access = (hazard_if.dren || hazard_if.dwen); + assign branch_jump = hazard_if.jump || (hazard_if.branch && hazard_if.mispredict); + assign wait_for_imem = hazard_if.iren & hazard_if.i_mem_busy; + assign wait_for_dmem = dmem_access & hazard_if.d_mem_busy; + assign mem_use_stall = hazard_if.reg_write && cannot_forward && (rs1_match || rs2_match); + + assign hazard_if.npc_sel = branch_jump; + assign hazard_if.pc_en = (~wait_for_dmem & ~wait_for_imem & ~hazard_if.halt & ~ex_flush_hazard + & ~rmgmt_stall & ~hazard_if.fence_stall) + | branch_jump | prv_pipe_if.insert_pc | prv_pipe_if.ret; + + + /* Hazards due to Interrupts/Exceptions */ + assign prv_pipe_if.ret = hazard_if.ret; + assign exception = hazard_if.fault_insn | hazard_if.mal_insn | prv_pipe_if.prot_fault_i + | hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l + | hazard_if.fault_s | hazard_if.mal_s | hazard_if.breakpoint + | hazard_if.env_m | prv_pipe_if.prot_fault_l | prv_pipe_if.prot_fault_s; + + assign intr = ~exception & prv_pipe_if.intr; + + assign prv_pipe_if.pipe_clear = exception | ~(hazard_if.token_ex | rm_if.active_insn); + assign ex_flush_hazard = ((intr | exception) & ~wait_for_dmem) | exception | prv_pipe_if.ret; //TODO: Check this + + assign hazard_if.insert_priv_pc = prv_pipe_if.insert_pc; + assign hazard_if.priv_pc = prv_pipe_if.priv_pc; + + assign hazard_if.iren = !intr && !branch_jump; // prevents a false instruction request from being sent + + /* Send Exception notifications to Prv Block */ + // TODO: Correct execution of exceptions + assign prv_pipe_if.wb_enable = !hazard_if.if_ex_stall | + hazard_if.jump | + hazard_if.branch; //Because 2 stages + + assign prv_pipe_if.fault_insn = hazard_if.fault_insn | prv_pipe_if.prot_fault_i; + assign prv_pipe_if.mal_insn = hazard_if.mal_insn; + assign prv_pipe_if.illegal_insn = hazard_if.illegal_insn; + assign prv_pipe_if.fault_l = hazard_if.fault_l | prv_pipe_if.prot_fault_l; + assign prv_pipe_if.mal_l = hazard_if.mal_l; + assign prv_pipe_if.fault_s = hazard_if.fault_s | prv_pipe_if.prot_fault_s; + assign prv_pipe_if.mal_s = hazard_if.mal_s; + assign prv_pipe_if.breakpoint = hazard_if.breakpoint; + assign prv_pipe_if.env_m = hazard_if.env_m; + assign prv_pipe_if.ex_rmgmt = rm_if.exception; + + assign prv_pipe_if.ex_rmgmt_cause = rm_if.ex_cause; + assign prv_pipe_if.epc = hazard_if.epc; + assign prv_pipe_if.badaddr = hazard_if.badaddr; + + + /* + * Pipeline control signals + * + * Control hazard (Ex, Int, Jump, Mispredict): F/E -> Flush, E/M -> Flush + * Data hazard (unforwardable, load/CSR read): F/E -> Stall, E/M -> Flush + * Waiting (i.e. slow dmem access, fence, etc.): + * - If fetch stage slow, flush if_ex so in-flight instructions may finish (insert bubbles) + * - If ex stage slow, flush ex_mem so in-flight instruction may finish (insert bubbles). Stall if_ex + * - If mem stage slow, stall everyone + * - Halt - stall everyone + * Note: Stall of later stage implies stall of earlier stage. + */ + + assign hazard_if.if_ex_flush = ex_flush_hazard // control hazard + || branch_jump // control hazard + || (wait_for_imem && !hazard_if.ex_mem_stall); // Flush if fetch stage lagging, but ex/mem are moving + + assign hazard_if.ex_mem_flush = ex_flush_hazard // Control hazard + || branch_jump // Control hazard + //|| (mem_use_stall && !hazard_if.d_mem_busy) // Data hazard -- flush once data memory is no longer busy (request complete) + || (hazard_if.if_ex_stall && !hazard_if.ex_mem_stall); // if_ex_stall covers mem_use stall condition + + + assign hazard_if.if_ex_stall = hazard_if.ex_mem_stall // Stall this stage if next stage is stalled + // || (wait_for_imem && !dmem_access) // ??? + //& (~ex_flush_hazard | e_ex_stage) // ??? + || rm_if.execute_stall // + || mem_use_stall; // Data hazard -- stall until stall clears (from E/M flush after writeback) + // TODO: Exceptions + assign hazard_if.ex_mem_stall = wait_for_dmem + || hazard_if.fence_stall + || hazard_if.halt; + //|| branch_jump && wait_for_imem; // This can be removed once there is I$. Solves problem where + // stale I-request returns after PC is redirected + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + assign sparce_if.if_ex_enable = rm_if.if_ex_enable; + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv new file mode 100644 index 000000000..61a8e7a9d --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -0,0 +1,238 @@ + +`include "stage3_hazard_unit_if.vh" +`include "stage3_mem_pipe_if.vh" +`include "generic_bus_if.vh" +`include "predictor_pipeline_if.vh" +`include "cache_control_if.vh" +`include "prv_pipeline_if.vh" + +module stage3_mem_stage( + input CLK, + input nRST, + stage3_mem_pipe_if.mem ex_mem_if, + stage3_hazard_unit_if.mem hazard_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if.pipe prv_pipe_if, + cache_control_if.pipeline cc_if, + predictor_pipeline_if.update predict_if, + output logic halt, + output logic wfi +); + + import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; + + /*************** + * Branch Update + ****************/ + assign predict_if.update_predictor = ex_mem_if.ex_mem_reg.branch; + assign predict_if.prediction = ex_mem_if.ex_mem_reg.prediction; + assign predict_if.branch_result = ex_mem_if.ex_mem_reg.branch_taken; + assign predict_if.update_addr = ex_mem_if.ex_mem_reg.brj_addr; + + + + + /************* + * Data Access + **************/ + word_t store_swapped; + word_t dload_ext; + logic mal_addr; + logic [1:0] byte_offset; + logic [3:0] byte_en, byte_en_temp, byte_en_standard; + + + + // TODO: RISC-MGMT + assign dgen_bus_if.ren = ex_mem_if.ex_mem_reg.dren & ~mal_addr & ~prv_pipe_if.prot_fault_l; + assign dgen_bus_if.wen = ex_mem_if.ex_mem_reg.dwen & ~mal_addr & ~prv_pipe_if.prot_fault_s; + assign dgen_bus_if.byte_en = byte_en; + assign dgen_bus_if.addr = ex_mem_if.ex_mem_reg.port_out; + assign byte_offset = ex_mem_if.ex_mem_reg.port_out[1:0]; + + // TODO: RISC-MGMT + assign byte_en_temp = byte_en_standard; + + // Address alignment + always_comb begin + if (byte_en == 4'hf) mal_addr = (dgen_bus_if.addr[1:0] != 2'b00); + else if (byte_en == 4'h3 || byte_en == 4'hc) begin + mal_addr = (dgen_bus_if.addr[1:0] == 2'b01 || dgen_bus_if.addr[1:0] == 2'b11); + end else mal_addr = 1'b0; + end + + endian_swapper store_swap( + .word_in(ex_mem_if.ex_mem_reg.rs2_data), + .word_out(store_swapped) + ); + + dmem_extender dmem_ext( + .dmem_in(dgen_bus_if.rdata), + .load_type(ex_mem_if.ex_mem_reg.load_type), + .byte_en(byte_en), + .ext_out(dload_ext) + ); + + always_comb begin : LOAD_TYPE + case (ex_mem_if.ex_mem_reg.load_type) + LB, LBU: begin + case (byte_offset) + 2'b00: byte_en_standard = 4'b0001; + 2'b01: byte_en_standard = 4'b0010; + 2'b10: byte_en_standard = 4'b0100; + 2'b11: byte_en_standard = 4'b1000; + default: byte_en_standard = 4'b0000; + endcase + end + + LH, LHU: begin + case (byte_offset) + 2'b00: byte_en_standard = 4'b0011; + 2'b10: byte_en_standard = 4'b1100; + default: byte_en_standard = 4'b0000; + endcase + end + + LW: begin + byte_en_standard = 4'b1111; + end + + default: byte_en_standard = 4'b0000; + endcase + end : LOAD_TYPE + + // TODO: RISC-MGMT + always_comb begin : STORE_TYPE + case(ex_mem_if.ex_mem_reg.load_type) + LB: dgen_bus_if.wdata = {4{ex_mem_if.ex_mem_reg.rs2_data[7:0]}}; + LH: dgen_bus_if.wdata = {2{ex_mem_if.ex_mem_reg.rs2_data[15:0]}}; + LW: dgen_bus_if.wdata = ex_mem_if.ex_mem_reg.rs2_data; + default: dgen_bus_if.wdata = '0; + endcase + end : STORE_TYPE + + // Endianness + generate + if(BUS_ENDIANNESS == "big") begin : g_data_bus_be + assign byte_en = byte_en_temp; + end else if(BUS_ENDIANNESS == "little") begin : g_data_bus_le + assign byte_en = ex_mem_if.ex_mem_reg.dren ? byte_en_temp + : {byte_en_temp[0], byte_en_temp[1], + byte_en_temp[2], byte_en_temp[3]}; + end + endgenerate + + + /****************** + * Cache management + *******************/ + logic ifence_reg; + logic ifence_pulse; + logic iflushed, iflushed_next; + logic dflushed, dflushed_next; + + always_ff @(posedge CLK, negedge nRST) begin + if(!nRST) begin + ifence_reg <= 1'b0; + iflushed <= 1'b1; + dflushed <= 1'b1; + end else begin + ifence_reg <= ex_mem_if.ex_mem_reg.ifence; + iflushed <= iflushed_next; + dflushed <= dflushed_next; + end + end + + assign ifence_pulse = ex_mem_if.ex_mem_reg.ifence & ~ifence_reg; + assign iflushed_next = ifence_pulse ? 1'b0 : cc_if.iflush_done; + assign dflushed_next = ifence_pulse ? 1'b0 : cc_if.dflush_done; + + /************* + * Hazard Unit + **************/ + // Note: Some hazard unit signals are assigned below in the CSR section + assign hazard_if.d_mem_busy = dgen_bus_if.busy; + assign hazard_if.fence_stall = ex_mem_if.ex_mem_reg.ifence && (!dflushed || !iflushed); + assign hazard_if.dren = ex_mem_if.ex_mem_reg.dren; + assign hazard_if.dwen = ex_mem_if.ex_mem_reg.dwen; + assign hazard_if.jump = ex_mem_if.ex_mem_reg.jump; + assign hazard_if.branch = ex_mem_if.ex_mem_reg.branch; + assign hazard_if.halt = ex_mem_if.ex_mem_reg.halt; + assign hazard_if.rd_m = ex_mem_if.ex_mem_reg.rd_m; + assign hazard_if.reg_write = ex_mem_if.ex_mem_reg.reg_write; + assign hazard_if.csr_read = prv_pipe_if.valid_write; + assign hazard_if.token_mem = 0; // TODO: RISC-MGMT + assign hazard_if.mispredict = ex_mem_if.ex_mem_reg.prediction ^ ex_mem_if.ex_mem_reg.branch_taken; + assign hazard_if.pc = ex_mem_if.ex_mem_reg.pc; + + assign halt = ex_mem_if.ex_mem_reg.halt; + + + /****** + * CSRs + *******/ + assign prv_pipe_if.swap = ex_mem_if.ex_mem_reg.csr_swap; + assign prv_pipe_if.clr = ex_mem_if.ex_mem_reg.csr_clr; + assign prv_pipe_if.set = ex_mem_if.ex_mem_reg.csr_set; + assign prv_pipe_if.wdata = ex_mem_if.ex_mem_reg.csr_imm ? {27'h0, ex_mem_if.ex_mem_reg.zimm} : ex_mem_if.ex_mem_reg.rs1_data; + assign prv_pipe_if.csr_addr = ex_mem_if.ex_mem_reg.csr_addr; + assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr + | prv_pipe_if.set) & ~hazard_if.ex_mem_stall; + assign prv_pipe_if.instr = (ex_mem_if.ex_mem_reg.instr != '0); + + assign hazard_if.fault_l = 1'b0; + assign hazard_if.mal_l = ex_mem_if.ex_mem_reg.dren & mal_addr; + assign hazard_if.fault_s = 1'b0; + assign hazard_if.mal_s = ex_mem_if.ex_mem_reg.dwen & mal_addr; + assign hazard_if.breakpoint = ex_mem_if.ex_mem_reg.breakpoint; + assign hazard_if.env_m = ex_mem_if.ex_mem_reg.ecall_insn; + assign hazard_if.ret = ex_mem_if.ex_mem_reg.ret_insn; + assign hazard_if.badaddr = dgen_bus_if.addr; + + // NEW + assign hazard_if.epc = ex_mem_if.ex_mem_reg.pc; + + // Memory protection (doesn't consider RISC-MGMT) + assign prv_pipe_if.dren = ex_mem_if.ex_mem_reg.dren & ~mal_addr; + assign prv_pipe_if.dwen = ex_mem_if.ex_mem_reg.dwen & ~mal_addr; + assign prv_pipe_if.daddr = ex_mem_if.ex_mem_reg.port_out; + assign prv_pipe_if.d_acc_width = WordAcc; + + // TODO: Currently omitting SparCE + + /*********** + * Writeback + ************/ + assign ex_mem_if.brj_addr = ex_mem_if.ex_mem_reg.brj_addr; + assign ex_mem_if.reg_write = ex_mem_if.ex_mem_reg.reg_write; + assign ex_mem_if.rd_m = ex_mem_if.ex_mem_reg.rd_m; + + always_comb begin + // TODO: RISC-MGMT + case (ex_mem_if.ex_mem_reg.w_sel) + 3'd0: ex_mem_if.reg_wdata = dload_ext; + 3'd1: ex_mem_if.reg_wdata = ex_mem_if.ex_mem_reg.pc4; + 3'd2: ex_mem_if.reg_wdata = ex_mem_if.ex_mem_reg.imm_U; + 3'd3: ex_mem_if.reg_wdata = ex_mem_if.ex_mem_reg.port_out; + 3'd4: ex_mem_if.reg_wdata = prv_pipe_if.rdata; + default: ex_mem_if.reg_wdata = '0; + endcase + end + + /************** + * CPU Tracking + ***************/ + logic wb_stall; + logic [2:0] funct3; + logic [11:0] funct12; + logic instr_30; + + // TODO: Fix up hazard unit + assign funct3 = ex_mem_if.ex_mem_reg.instr[14:12]; + assign funct12 = ex_mem_if.ex_mem_reg.instr[31:20]; + assign instr_30 = ex_mem_if.ex_mem_reg.instr[30]; + assign wb_stall = hazard_if.ex_mem_stall & ~hazard_if.jump & ~hazard_if.branch; // TODO: Is this right? + + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_types_pkg.sv b/source_code/pipelines/stage3/source/stage3_types_pkg.sv new file mode 100644 index 000000000..1d6b158c8 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_types_pkg.sv @@ -0,0 +1,54 @@ + +package stage3_types_pkg; + + import rv32i_types_pkg::*; + import machine_mode_types_1_12_pkg::*; + + typedef struct packed { + logic token; + logic mal_insn; + logic fault_insn; + word_t pc; + word_t pc4; + word_t instr; + word_t prediction; + word_t badaddr; + } fetch_ex_t; + + // TODO: Instructions? + typedef struct packed { + logic branch; + logic prediction; + logic branch_taken; + logic dren; + logic dwen; + logic reg_write; + logic ifence; + logic jump; + logic halt; + logic csr_swap; + logic csr_clr; + logic csr_set; + logic csr_imm; + logic breakpoint; + logic ecall_insn; + logic ret_insn; + logic was_compressed; // Determine if PC should advance by 4 or 2, avoid passing PC and PC + (2/4) through pipeline + logic [2:0] w_sel; + logic [3:0] byte_en; // TODO: Where should this be generated? + logic [4:0] zimm; + logic [4:0] rd_m; + load_t load_type; + csr_addr_t csr_addr; + word_t brj_addr; + word_t port_out; + word_t rs1_data; + word_t rs2_data; + word_t instr; + word_t pc; + word_t pc4; + word_t imm_U; + // TODO: imm_U? Maybe needed + } ex_mem_t; + +endpackage diff --git a/source_code/pipelines/stage3/stage3.core b/source_code/pipelines/stage3/stage3.core new file mode 100644 index 000000000..7f34a96dd --- /dev/null +++ b/source_code/pipelines/stage3/stage3.core @@ -0,0 +1,36 @@ +CAPI=2: +name: socet:riscv:stage3:0.1.0 +description: Two-stage pipeline + +filesets: + rtl: + files: + - source/stage3_types_pkg.sv + - include/stage3_fetch_execute_if.vh : {is_include_file: true} + - include/stage3_mem_pipe_if.vh : {is_include_file: true} + - include/stage3_hazard_unit_if.vh : {is_include_file: true} + - include/stage3_forwarding_unit_if.vh : {is_include_file: true} + - source/stage3_fetch_stage.sv + - source/stage3_execute_stage.sv + - source/stage3_mem_stage.sv + - source/stage3_hazard_unit.sv + - source/stage3_forwarding_unit.sv + - source/stage3.sv + file_type: systemVerilogSource + + +targets: + default: &default + filesets: + - rtl + toplevel: stage3 + + lint: + <<: *default + description: Linting + default_tool: veriblelint + toplevel: stage3 + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search', '--waiver_files=stage3.waiver'] + diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index 6f3da360a..161e703a7 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -68,6 +68,12 @@ module RISCVBusiness ( tspp_fetch_execute_if fetch_ex_if (); tspp_hazard_unit_if hazard_if (); + stage3 #(.RESET_PC(RESET_PC)) pipeline( + .igen_bus_if(tspp_icache_gen_bus_if), + .dgen_bus_if(tspp_dcache_gen_bus_if), + .* + ); + // Module Instantiations /* pipeline_wrapper pipeline ( @@ -84,6 +90,7 @@ module RISCVBusiness ( ); */ +/* tspp_fetch_stage #( .RESET_PC(RESET_PC) ) fetch_stage_i ( @@ -120,7 +127,7 @@ module RISCVBusiness ( .rm_if(rm_if), .sparce_if(sparce_if) ); - +*/ branch_predictor_wrapper branch_predictor_i ( .CLK(CLK), diff --git a/source_code/standard_core/top_core.sv b/source_code/standard_core/top_core.sv index 82ef8eef4..04cd05f13 100644 --- a/source_code/standard_core/top_core.sv +++ b/source_code/standard_core/top_core.sv @@ -1,5 +1,6 @@ `include "core_interrupt_if.vh" `include "generic_bus_if.vh" +`include "component_selection_defines.vh" module top_core #( parameter logic [31:0] RESET_PC = 32'h80000000 @@ -21,6 +22,8 @@ module top_core #( // ahb if case `elsif BUS_INTERFACE_AHB // TODO +`else + `endif // core_interrupt_if input ext_int, @@ -34,28 +37,27 @@ module top_core #( function [31:0] get_x28; // verilator public - get_x28 = CORE.execute_stage_i.g_rfile_select.rf.registers[28]; + get_x28 = CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[28]; endfunction - /* - bind tspp_execute_stage cpu_tracker cpu_track1 ( + bind stage3_mem_stage cpu_tracker cpu_track1 ( .CLK(CLK), .wb_stall(wb_stall), - .instr(fetch_ex_if.fetch_ex_reg.instr), - .pc(fetch_ex_if.fetch_ex_reg.pc), - .opcode(cu_if.opcode), + .instr(ex_mem_if.ex_mem_reg.instr), + .pc(ex_mem_if.ex_mem_reg.pc), + .opcode(rv32i_types_pkg::opcode_t'(ex_mem_if.ex_mem_reg.instr[6:0])), .funct3(funct3), .funct12(funct12), - .rs1(rf_if.rs1), - .rs2(rf_if.rs2), - .rd(rf_if.rd), - .imm_S(cu_if.imm_S), - .imm_I(cu_if.imm_I), - .imm_U(cu_if.imm_U), - .imm_UJ(imm_UJ_ext), - .imm_SB(cu_if.imm_SB), + .rs1(ex_mem_if.ex_mem_reg.instr[19:15]), + .rs2(ex_mem_if.ex_mem_reg.instr[24:20]), + .rd(ex_mem_if.ex_mem_reg.rd_m), + .imm_S('0), // TODO: Extract constants. Maybe we could pass these in the pipeline and they'd be removed by synthesis? + .imm_I('0), + .imm_U('0), + .imm_UJ('0), + .imm_SB('0), .instr_30(instr_30) - );*/ + ); @@ -82,6 +84,8 @@ module top_core #( `elsif BUS_INTERFACE_APB apb_if apb_requester (CLK, nRST); +`else + `endif diff --git a/wave_format.gtkw b/wave_format.gtkw new file mode 100644 index 000000000..41a66e083 --- /dev/null +++ b/wave_format.gtkw @@ -0,0 +1,141 @@ +[*] +[*] GTKWave Analyzer v3.3.111 (w)1999-2020 BSI +[*] Sat Nov 5 21:49:30 2022 +[*] +[dumpfile] "/home/asicfab/a/socet46/RISCVBusiness/waveform.fst" +[dumpfile_mtime] "Sat Nov 5 21:33:40 2022" +[dumpfile_size] 28545 +[savefile] "/home/asicfab/a/socet46/RISCVBusiness/restore.gtkw" +[timestart] 0 +[size] 1000 600 +[pos] 930 20 +*-4.008905 42 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] TOP. +[treeopen] TOP.top_core. +[treeopen] TOP.top_core.CORE. +[treeopen] TOP.top_core.CORE.pipeline. +[treeopen] TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf. +[sst_width] 293 +[signals_width] 275 +[sst_expanded] 1 +[sst_vpaned_height] 143 +@28 +[color] 3 +TOP.CLK +[color] 3 +TOP.nRST +[color] 3 +TOP.halt +@200 +- +-FETCH STAGE +@22 +TOP.top_core.CORE.pipeline.fetch_stage_i.pc[31:0] +TOP.top_core.CORE.pipeline.fetch_stage_i.instr[31:0] +@28 +TOP.top_core.CORE.pipeline.fetch_stage_i.mal_insn +@22 +TOP.top_core.CORE.pipeline.fetch_stage_i.badaddr[31:0] +TOP.top_core.CORE.pipeline.fetch_stage_i.pc4or2[31:0] +@200 +- +@22 +TOP.top_core.CORE.pipeline.mem_pipe_if.brj_addr[31:0] +@200 +- +-EXECUTE STAGE +@100000028 +TOP.top_core.CORE.pipeline.execute_stage_i.cu_if.opcode[6:0] +TOP.top_core.CORE.pipeline.execute_stage_i.cu_if.branch_type[2:0] +TOP.top_core.CORE.pipeline.execute_stage_i.cu_if.load_type[2:0] +@22 +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.rf_if.rs1[4:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.rf_if.rs2[4:0] +@200 +- +@100000028 +TOP.top_core.CORE.pipeline.execute_stage_i.alu_if.aluop[3:0] +@22 +TOP.top_core.CORE.pipeline.execute_stage_i.alu_if.port_out[31:0] +@200 +- +@28 +TOP.top_core.CORE.pipeline.execute_stage_i.branch_if.branch_taken +@22 +TOP.top_core.CORE.pipeline.execute_stage_i.branch_if.branch_addr[31:0] +@200 +- +@22 +TOP.top_core.CORE.pipeline.execute_stage_i.ex_mem_if.rd_m[4:0] +TOP.top_core.CORE.pipeline.execute_stage_i.ex_mem_if.reg_wdata[31:0] +@28 +TOP.top_core.CORE.pipeline.execute_stage_i.ex_mem_if.reg_write +@200 +- +-MEM STAGE +@22 +TOP.top_core.CORE.pipeline.mem_stage_i.ex_mem_if.ex_mem_reg.pc[31:0] +@23 +TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.addr[31:0] +@28 +TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.busy +@22 +TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.byte_en[3:0] +TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.rdata[31:0] +@28 +TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.ren +@22 +TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.wdata[31:0] +@28 +TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.wen +@200 +- +-HAZARD & FORWARDING +@28 +TOP.top_core.CORE.pipeline.fw_if.fwd_rs1 +TOP.top_core.CORE.pipeline.fw_if.fwd_rs2 +@200 +- +@28 +TOP.top_core.CORE.pipeline.hazard_if.if_ex_flush +TOP.top_core.CORE.pipeline.hazard_if.if_ex_stall +TOP.top_core.CORE.pipeline.hazard_if.ex_mem_flush +TOP.top_core.CORE.pipeline.hazard_if.ex_mem_stall +@200 +- +-REGISTER FILE +@22 +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[0][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[1][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[2][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[3][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[4][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[5][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[6][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[7][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[8][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[9][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[10][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[11][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[12][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[13][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[14][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[15][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[16][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[17][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[18][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[19][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[20][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[21][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[22][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[23][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[24][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[25][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[26][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[27][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[28][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[29][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[30][31:0] +TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[31][31:0] +[pattern_trace] 1 +[pattern_trace] 0 From f0d37ccb5590d745a69151784f4b0ea3bcf7dc10 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sat, 5 Nov 2022 22:15:33 -0400 Subject: [PATCH 21/47] stage3: Add full CPU tracker support This commit adds debug-only signals to the pipeline for full CPU tracker support. Additionally allows the TB to dump the waveform on a CTL-C (SIGINT) instead of leaving a corrupted waveform trace for better debugging of infinite loops. --- .gitignore | 3 ++- .../stage3/source/stage3_execute_stage.sv | 8 ++++++ .../stage3/source/stage3_types_pkg.sv | 10 +++++++ source_code/standard_core/top_core.sv | 10 +++---- tb_core.cc | 27 ++++++++++++++----- 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index dab1eb6d5..8f490c0e0 100644 --- a/.gitignore +++ b/.gitignore @@ -68,4 +68,5 @@ fusesoc_libraries fusesoc.conf # synthesis scripts -desflow/** +desflow1/ +.vscode/ diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index c71c9e419..e1028e814 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -305,6 +305,14 @@ module stage3_execute_stage ( ex_mem_if.ex_mem_reg.pc4 <= fetch_ex_if.fetch_ex_reg.pc4; ex_mem_if.ex_mem_reg.imm_U <= cu_if.imm_U; + // CPU Tracker + ex_mem_if.ex_mem_reg.tracker_signals.opcode <= cu_if.opcode; + ex_mem_if.ex_mem_reg.tracker_signals.imm_SB <= cu_if.imm_SB; + ex_mem_if.ex_mem_reg.tracker_signals.imm_S <= cu_if.imm_S; + ex_mem_if.ex_mem_reg.tracker_signals.imm_I <= cu_if.imm_I; + ex_mem_if.ex_mem_reg.tracker_signals.imm_UJ <= cu_if.imm_UJ; + ex_mem_if.ex_mem_reg.tracker_signals.imm_U <= cu_if.imm_U; + end else if(hazard_if.ex_mem_flush && !hazard_if.ex_mem_stall) begin ex_mem_if.ex_mem_reg <= '{default: '0}; end diff --git a/source_code/pipelines/stage3/source/stage3_types_pkg.sv b/source_code/pipelines/stage3/source/stage3_types_pkg.sv index 1d6b158c8..bfdbc0892 100644 --- a/source_code/pipelines/stage3/source/stage3_types_pkg.sv +++ b/source_code/pipelines/stage3/source/stage3_types_pkg.sv @@ -15,6 +15,15 @@ package stage3_types_pkg; word_t badaddr; } fetch_ex_t; + typedef struct packed { + opcode_t opcode; + logic [12:0] imm_SB; + logic [11:0] imm_S; + logic [11:0] imm_I; + logic [20:0] imm_UJ; + logic [31:0] imm_U; + } tracker_ex_mem_t; + // TODO: Instructions? typedef struct packed { logic branch; @@ -48,6 +57,7 @@ package stage3_types_pkg; word_t pc; word_t pc4; word_t imm_U; + tracker_ex_mem_t tracker_signals; // TODO: imm_U? Maybe needed } ex_mem_t; diff --git a/source_code/standard_core/top_core.sv b/source_code/standard_core/top_core.sv index 04cd05f13..2a862ee9f 100644 --- a/source_code/standard_core/top_core.sv +++ b/source_code/standard_core/top_core.sv @@ -51,11 +51,11 @@ module top_core #( .rs1(ex_mem_if.ex_mem_reg.instr[19:15]), .rs2(ex_mem_if.ex_mem_reg.instr[24:20]), .rd(ex_mem_if.ex_mem_reg.rd_m), - .imm_S('0), // TODO: Extract constants. Maybe we could pass these in the pipeline and they'd be removed by synthesis? - .imm_I('0), - .imm_U('0), - .imm_UJ('0), - .imm_SB('0), + .imm_S(ex_mem_if.ex_mem_reg.tracker_signals.imm_S), // TODO: Extract constants. Maybe we could pass these in the pipeline and they'd be removed by synthesis? + .imm_I(ex_mem_if.ex_mem_reg.tracker_signals.imm_I), + .imm_U(ex_mem_if.ex_mem_reg.tracker_signals.imm_U), + .imm_UJ(ex_mem_if.ex_mem_reg.tracker_signals.imm_UJ), + .imm_SB(ex_mem_if.ex_mem_reg.tracker_signals.imm_SB), .instr_30(instr_30) ); diff --git a/tb_core.cc b/tb_core.cc index a54609f9b..e99ca605d 100644 --- a/tb_core.cc +++ b/tb_core.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include "verilated.h" #include "verilated_fst_c.h" @@ -25,6 +26,8 @@ // doubles as mtime counter vluint64_t sim_time = 0; +Vtop_core *dut_ptr; +VerilatedFstC *trace_ptr; /* * Emulate memory-mapped CSRs @@ -35,7 +38,12 @@ uint32_t msip = 0; // Interrupt signals bool ext_int = false; - +void signal_handler(int signum) { + std::cout << "Got signal " << signum << std::endl; + dut_ptr->final(); + trace_ptr->close(); + exit(1); +} class MemoryMap { private: @@ -169,10 +177,10 @@ class MemoryMap { for(auto p : mmap) { if(p.second != 0) { char buf[80]; - snprintf(buf, 80, "%08x : %02x%02x%02x%02x", p.first, - (p.second & 0xFF000000) >> 24, - (p.second & 0x00FF0000) >> 16, - (p.second & 0x0000FF00) >> 8, + snprintf(buf, 80, "%08x : %02x%02x%02x%02x", p.first, + (p.second & 0xFF000000) >> 24, + (p.second & 0x00FF0000) >> 16, + (p.second & 0x0000FF00) >> 8, p.second & 0x000000FF); outfile << buf << std::endl; } @@ -221,7 +229,7 @@ void tick(Vtop_core& dut, VerilatedFstC& trace) { } void reset(Vtop_core& dut, VerilatedFstC& trace) { - // Initialize signals + // Initialize signals dut.CLK = 0; dut.nRST = 0; dut.ext_int = 0; @@ -264,6 +272,13 @@ int main(int argc, char **argv) { mtimecmp = 0xFFFFFFFFFFFFFFFF; // Default to a massive value + + dut_ptr = &dut; + trace_ptr = &m_trace; + + signal(SIGINT, signal_handler); + + reset(dut, m_trace); while(!dut.halt && sim_time < 100000) { // TODO: Variable latency From f43b408e0f6df7d116af1c729bff0185f245d95d Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sun, 6 Nov 2022 00:24:01 -0400 Subject: [PATCH 22/47] stage3: exceptions functional All existing synchronous exception tests pass (ecall, pma). Additionally, 2 more tests for illegal instructions and pma i-fetch faults were added to test exceptions originating in all 3 stages, which also pass. --- .../stage3/include/stage3_hazard_unit_if.vh | 10 ++--- .../stage3/source/stage3_execute_stage.sv | 43 +++++++++++-------- .../stage3/source/stage3_fetch_stage.sv | 6 +-- .../stage3/source/stage3_hazard_unit.sv | 9 ++-- .../stage3/source/stage3_mem_stage.sv | 13 +++--- .../stage3/source/stage3_types_pkg.sv | 4 ++ verification/interrupts-exceptions/illegal.c | 32 ++++++++++++++ verification/pma-tests/pma_i.c | 40 +++++++++++++++++ wave_format.gtkw | 24 +++++++---- 9 files changed, 138 insertions(+), 43 deletions(-) create mode 100644 verification/interrupts-exceptions/illegal.c create mode 100644 verification/pma-tests/pma_i.c diff --git a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh index 6b88fb566..4d407e2df 100644 --- a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh +++ b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh @@ -32,7 +32,7 @@ interface stage3_hazard_unit_if(); // Pipeline status signals (inputs) logic [4:0] rs1_e, rs2_e, rd_m; logic reg_write, csr_read; - logic i_mem_busy, d_mem_busy, dren, dwen, ret; + logic i_mem_busy, d_mem_busy, dren, dwen, ret, suppress_data; logic jump, branch, fence_stall; logic mispredict, halt; word_t pc; @@ -41,7 +41,7 @@ interface stage3_hazard_unit_if(); logic pc_en, npc_sel; logic if_ex_flush, ex_mem_flush; logic if_ex_stall, ex_mem_stall; - logic iren; + logic iren, suppress_iren; // xTVEC Insertion word_t priv_pc; @@ -70,11 +70,11 @@ interface stage3_hazard_unit_if(); output pc_en, npc_sel, if_ex_flush, ex_mem_flush, if_ex_stall, ex_mem_stall, - priv_pc, insert_priv_pc, iren + priv_pc, insert_priv_pc, iren, suppress_iren, suppress_data ); modport fetch ( - input pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren, + input pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren, suppress_iren, output i_mem_busy, rv32c_ready ); @@ -84,7 +84,7 @@ interface stage3_hazard_unit_if(); ); modport mem ( - input ex_mem_stall, ex_mem_flush, + input ex_mem_stall, ex_mem_flush, suppress_data, output rd_m, reg_write, csr_read, d_mem_busy, dren, dwen, ret, jump, branch, fence_stall, mispredict, halt, pc, diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index e1028e814..f410a155c 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -269,24 +269,31 @@ module stage3_execute_stage ( // PS: Does it even matter? Synth. tools may be able to merge regs. if(!hazard_if.ex_mem_flush && !hazard_if.ex_mem_stall) begin // TODO: Handle case of exceptions earlier in the pipe being passed on to handle in the last stage - // Single bit control signals - ex_mem_if.ex_mem_reg.branch <= cu_if.branch; - ex_mem_if.ex_mem_reg.prediction <= fetch_ex_if.fetch_ex_reg.prediction; - ex_mem_if.ex_mem_reg.branch_taken <= branch_if.branch_taken; - ex_mem_if.ex_mem_reg.dren <= cu_if.dren; - ex_mem_if.ex_mem_reg.dwen <= cu_if.dwen; - ex_mem_if.ex_mem_reg.reg_write <= cu_if.wen; - ex_mem_if.ex_mem_reg.ifence <= cu_if.ifence; - ex_mem_if.ex_mem_reg.jump <= cu_if.jump; - ex_mem_if.ex_mem_reg.halt <= cu_if.halt; - ex_mem_if.ex_mem_reg.csr_swap <= cu_if.csr_swap; - ex_mem_if.ex_mem_reg.csr_clr <= cu_if.csr_clr; - ex_mem_if.ex_mem_reg.csr_set <= cu_if.csr_set; - ex_mem_if.ex_mem_reg.csr_imm <= cu_if.csr_imm; - ex_mem_if.ex_mem_reg.breakpoint <= cu_if.breakpoint; - ex_mem_if.ex_mem_reg.ecall_insn <= cu_if.ecall_insn; - ex_mem_if.ex_mem_reg.ret_insn <= cu_if.ret_insn; - ex_mem_if.ex_mem_reg.was_compressed <= 1'b0; // TODO: RV32C support + // Single bit control signals -- squash these if we have an exception + // Only need to check illegal since it's the only "new" exception we have + if(!cu_if.illegal_insn) begin + ex_mem_if.ex_mem_reg.branch <= cu_if.branch; + ex_mem_if.ex_mem_reg.prediction <= fetch_ex_if.fetch_ex_reg.prediction; + ex_mem_if.ex_mem_reg.branch_taken <= branch_if.branch_taken; + ex_mem_if.ex_mem_reg.dren <= cu_if.dren; + ex_mem_if.ex_mem_reg.dwen <= cu_if.dwen; + ex_mem_if.ex_mem_reg.reg_write <= cu_if.wen; + ex_mem_if.ex_mem_reg.ifence <= cu_if.ifence; + ex_mem_if.ex_mem_reg.jump <= cu_if.jump; + ex_mem_if.ex_mem_reg.halt <= cu_if.halt; + ex_mem_if.ex_mem_reg.csr_swap <= cu_if.csr_swap; + ex_mem_if.ex_mem_reg.csr_clr <= cu_if.csr_clr; + ex_mem_if.ex_mem_reg.csr_set <= cu_if.csr_set; + ex_mem_if.ex_mem_reg.csr_imm <= cu_if.csr_imm; + ex_mem_if.ex_mem_reg.breakpoint <= cu_if.breakpoint; + ex_mem_if.ex_mem_reg.ecall_insn <= cu_if.ecall_insn; + ex_mem_if.ex_mem_reg.ret_insn <= cu_if.ret_insn; + ex_mem_if.ex_mem_reg.was_compressed <= 1'b0; // TODO: RV32C support + end + ex_mem_if.ex_mem_reg.illegal_insn <= cu_if.illegal_insn; + ex_mem_if.ex_mem_reg.badaddr <= fetch_ex_if.fetch_ex_reg.badaddr; + ex_mem_if.ex_mem_reg.mal_insn <= fetch_ex_if.fetch_ex_reg.mal_insn; + ex_mem_if.ex_mem_reg.fault_insn <= fetch_ex_if.fetch_ex_reg.fault_insn; // Bit vectors ex_mem_if.ex_mem_reg.w_sel <= cu_if.w_sel; diff --git a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv index 0f723efe3..6a530557e 100644 --- a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv @@ -85,7 +85,7 @@ module stage3_fetch_stage ( //Instruction Access logic assign hazard_if.i_mem_busy = igen_bus_if.busy; assign igen_bus_if.addr = rv32cif.rv32c_ena ? rv32cif.imem_pc : pc; - assign igen_bus_if.ren = hazard_if.iren & !rv32cif.done_earlier; + assign igen_bus_if.ren = hazard_if.iren && !rv32cif.done_earlier && !hazard_if.suppress_iren; assign igen_bus_if.wen = 1'b0; assign igen_bus_if.byte_en = 4'b1111; assign igen_bus_if.wdata = '0; @@ -99,7 +99,7 @@ module stage3_fetch_stage ( assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); assign fault_insn = 1'b0; assign mal_insn = mal_addr; - assign badaddr_f = igen_bus_if.addr; + assign badaddr = igen_bus_if.addr; //Fetch Execute Pipeline Signals @@ -123,7 +123,7 @@ module stage3_fetch_stage ( // Send memory protection signals - assign prv_pipe_if.iren = igen_bus_if.ren; + assign prv_pipe_if.iren = hazard_if.iren; assign prv_pipe_if.iaddr = igen_bus_if.addr; assign prv_pipe_if.i_acc_width = WordAcc; diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv index df8c6b39e..2e52fd709 100644 --- a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -67,8 +67,8 @@ module stage3_hazard_unit ( assign dmem_access = (hazard_if.dren || hazard_if.dwen); assign branch_jump = hazard_if.jump || (hazard_if.branch && hazard_if.mispredict); - assign wait_for_imem = hazard_if.iren & hazard_if.i_mem_busy; - assign wait_for_dmem = dmem_access & hazard_if.d_mem_busy; + assign wait_for_imem = hazard_if.iren && hazard_if.i_mem_busy && !hazard_if.suppress_data; + assign wait_for_dmem = dmem_access && hazard_if.d_mem_busy && !hazard_if.suppress_iren; assign mem_use_stall = hazard_if.reg_write && cannot_forward && (rs1_match || rs2_match); assign hazard_if.npc_sel = branch_jump; @@ -92,7 +92,9 @@ module stage3_hazard_unit ( assign hazard_if.insert_priv_pc = prv_pipe_if.insert_pc; assign hazard_if.priv_pc = prv_pipe_if.priv_pc; - assign hazard_if.iren = !intr && !branch_jump; // prevents a false instruction request from being sent + assign hazard_if.iren = 1'b1; + assign hazard_if.suppress_iren = intr || branch_jump || exception || prv_pipe_if.ret; // prevents a false instruction request from being sent when pipeline flush imminent + assign hazard_if.suppress_data = intr || exception; // suppress data transfer on interrupt/exception. Exception case: prevent read/write of faulting location. Interrupt: make symmetric with exceptions for ease /* Send Exception notifications to Prv Block */ // TODO: Correct execution of exceptions @@ -150,6 +152,7 @@ module stage3_hazard_unit ( || hazard_if.halt; //|| branch_jump && wait_for_imem; // This can be removed once there is I$. Solves problem where // stale I-request returns after PC is redirected + // TODO: Enforce mutual exclusivity of these signals with assertion /********************************************************* *** SparCE Module Logic diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index 61a8e7a9d..3b450560c 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -45,8 +45,8 @@ module stage3_mem_stage( // TODO: RISC-MGMT - assign dgen_bus_if.ren = ex_mem_if.ex_mem_reg.dren & ~mal_addr & ~prv_pipe_if.prot_fault_l; - assign dgen_bus_if.wen = ex_mem_if.ex_mem_reg.dwen & ~mal_addr & ~prv_pipe_if.prot_fault_s; + assign dgen_bus_if.ren = ex_mem_if.ex_mem_reg.dren && !hazard_if.suppress_data; + assign dgen_bus_if.wen = ex_mem_if.ex_mem_reg.dwen && !hazard_if.suppress_data; assign dgen_bus_if.byte_en = byte_en; assign dgen_bus_if.addr = ex_mem_if.ex_mem_reg.port_out; assign byte_offset = ex_mem_if.ex_mem_reg.port_out[1:0]; @@ -181,6 +181,9 @@ module stage3_mem_stage( | prv_pipe_if.set) & ~hazard_if.ex_mem_stall; assign prv_pipe_if.instr = (ex_mem_if.ex_mem_reg.instr != '0); + assign hazard_if.fault_insn = ex_mem_if.ex_mem_reg.fault_insn; + assign hazard_if.mal_insn = ex_mem_if.ex_mem_reg.mal_insn; + assign hazard_if.illegal_insn = ex_mem_if.ex_mem_reg.illegal_insn; assign hazard_if.fault_l = 1'b0; assign hazard_if.mal_l = ex_mem_if.ex_mem_reg.dren & mal_addr; assign hazard_if.fault_s = 1'b0; @@ -188,14 +191,14 @@ module stage3_mem_stage( assign hazard_if.breakpoint = ex_mem_if.ex_mem_reg.breakpoint; assign hazard_if.env_m = ex_mem_if.ex_mem_reg.ecall_insn; assign hazard_if.ret = ex_mem_if.ex_mem_reg.ret_insn; - assign hazard_if.badaddr = dgen_bus_if.addr; + assign hazard_if.badaddr = (hazard_if.fault_insn || hazard_if.mal_insn) ? ex_mem_if.ex_mem_reg.badaddr : dgen_bus_if.addr; // NEW assign hazard_if.epc = ex_mem_if.ex_mem_reg.pc; // Memory protection (doesn't consider RISC-MGMT) - assign prv_pipe_if.dren = ex_mem_if.ex_mem_reg.dren & ~mal_addr; - assign prv_pipe_if.dwen = ex_mem_if.ex_mem_reg.dwen & ~mal_addr; + assign prv_pipe_if.dren = ex_mem_if.ex_mem_reg.dren; + assign prv_pipe_if.dwen = ex_mem_if.ex_mem_reg.dwen; assign prv_pipe_if.daddr = ex_mem_if.ex_mem_reg.port_out; assign prv_pipe_if.d_acc_width = WordAcc; diff --git a/source_code/pipelines/stage3/source/stage3_types_pkg.sv b/source_code/pipelines/stage3/source/stage3_types_pkg.sv index bfdbc0892..d0bb29e55 100644 --- a/source_code/pipelines/stage3/source/stage3_types_pkg.sv +++ b/source_code/pipelines/stage3/source/stage3_types_pkg.sv @@ -47,6 +47,9 @@ package stage3_types_pkg; logic [3:0] byte_en; // TODO: Where should this be generated? logic [4:0] zimm; logic [4:0] rd_m; + logic mal_insn; + logic fault_insn; + logic illegal_insn; load_t load_type; csr_addr_t csr_addr; word_t brj_addr; @@ -57,6 +60,7 @@ package stage3_types_pkg; word_t pc; word_t pc4; word_t imm_U; + word_t badaddr; tracker_ex_mem_t tracker_signals; // TODO: imm_U? Maybe needed } ex_mem_t; diff --git a/verification/interrupts-exceptions/illegal.c b/verification/interrupts-exceptions/illegal.c new file mode 100644 index 000000000..06ddb1a1f --- /dev/null +++ b/verification/interrupts-exceptions/illegal.c @@ -0,0 +1,32 @@ + +#include +#include "utility.h" + +extern volatile int flag; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + print("Made it to handler!"); + flag = 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t)handler; + uint32_t mstatus_value = 0x8; + + asm volatile("csrw mstatus, %0" : : "r"(mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r"(mtvec_value)); + + print("Executing illegal instruction\n"); + asm volatile(".word 0xFFFFFFFF"); + + print("Flag is 0x"); + put_uint32_hex(flag); + print("\n"); + + return 0; +} \ No newline at end of file diff --git a/verification/pma-tests/pma_i.c b/verification/pma-tests/pma_i.c new file mode 100644 index 000000000..089a9128f --- /dev/null +++ b/verification/pma-tests/pma_i.c @@ -0,0 +1,40 @@ +#include +#include "utility.h" + +extern volatile int flag; +extern void done(); + +#define PMA_ROM_ADDR 0x100 +#define PMA_RAM_ADDR 0x20000000 +volatile uint32_t *pma_rom_addr = (uint32_t*) PMA_ROM_ADDR; +volatile uint32_t *pma_ram_addr = (uint32_t*) PMA_RAM_ADDR; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value = (uint32_t)done; // return to the spot after we did the bad jump + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + print("PMA Checker failed (expected)\n"); + flag = 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + uint32_t mstatus_value = 0x8; + + // Disable all permissions for "rom" region of PMA + uint32_t pma_value = 0x3BF1 & ~(0x3800); // 0x3800 masks out RWX permissions + asm volatile("csrw 0xBC0, %0" : : "r"(pma_value)); + + // Set up interrupts + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + // After jump, instruction fetch will cause instruction fault + asm volatile("jr %0" : : "r" (pma_rom_addr)); + + // Never reached if successful since handler will jump directly to "done" + return 0; +} diff --git a/wave_format.gtkw b/wave_format.gtkw index 41a66e083..3f4dd3b32 100644 --- a/wave_format.gtkw +++ b/wave_format.gtkw @@ -1,24 +1,26 @@ [*] [*] GTKWave Analyzer v3.3.111 (w)1999-2020 BSI -[*] Sat Nov 5 21:49:30 2022 +[*] Sun Nov 6 02:28:56 2022 [*] [dumpfile] "/home/asicfab/a/socet46/RISCVBusiness/waveform.fst" -[dumpfile_mtime] "Sat Nov 5 21:33:40 2022" -[dumpfile_size] 28545 -[savefile] "/home/asicfab/a/socet46/RISCVBusiness/restore.gtkw" +[dumpfile_mtime] "Sun Nov 6 02:11:18 2022" +[dumpfile_size] 85451 +[savefile] "/home/asicfab/a/socet46/RISCVBusiness/wave_format.gtkw" [timestart] 0 -[size] 1000 600 -[pos] 930 20 -*-4.008905 42 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[size] 3440 1371 +[pos] -39 -39 +*-3.008905 52 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] TOP. [treeopen] TOP.top_core. [treeopen] TOP.top_core.CORE. [treeopen] TOP.top_core.CORE.pipeline. [treeopen] TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf. +[treeopen] TOP.top_core.CORE.pipeline.mem_pipe_if. +[treeopen] TOP.top_core.CORE.pipeline.mem_pipe_if.ex_mem_reg. [sst_width] 293 [signals_width] 275 [sst_expanded] 1 -[sst_vpaned_height] 143 +[sst_vpaned_height] 411 @28 [color] 3 TOP.CLK @@ -44,6 +46,8 @@ TOP.top_core.CORE.pipeline.mem_pipe_if.brj_addr[31:0] @200 - -EXECUTE STAGE +@22 +TOP.top_core.CORE.pipeline.fetch_ex_if.fetch_ex_reg.pc[31:0] @100000028 TOP.top_core.CORE.pipeline.execute_stage_i.cu_if.opcode[6:0] TOP.top_core.CORE.pipeline.execute_stage_i.cu_if.branch_type[2:0] @@ -75,7 +79,9 @@ TOP.top_core.CORE.pipeline.execute_stage_i.ex_mem_if.reg_write -MEM STAGE @22 TOP.top_core.CORE.pipeline.mem_stage_i.ex_mem_if.ex_mem_reg.pc[31:0] -@23 +@100000029 +TOP.top_core.CORE.pipeline.mem_pipe_if.ex_mem_reg.tracker_signals.opcode[6:0] +@22 TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.addr[31:0] @28 TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.busy From 3e4d85ab5ec2606835417edcff0f805c02a92a3e Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sun, 6 Nov 2022 12:15:32 -0500 Subject: [PATCH 23/47] stage3: Interrupt test passing This changes the logic for stalling/flushing so that on receiving an interrupt, the currently-executing memory instruction is allowed to finish (if such an instruction exists), then the oldest PC in the pipeline is taken. Since this is asynchronous, there is no guarantee the M-stage has a valid PC, so priority logic is needed to select from the 3 PCs in the pipeline. This is simpler than latching the next PC of the last valid instruction since it doesn't require instruction-specific knowledge (e.g. control flow target, compressed, etc.). Signals were added to the pipeline to track when an instruction is valid as well, since insn == 0 cannot be assumed to be a pipeline bubble instead of a nop instruction. --- .../stage3/include/stage3_hazard_unit_if.vh | 18 ++++++++------- .../stage3/source/stage3_execute_stage.sv | 3 +++ .../stage3/source/stage3_fetch_stage.sv | 2 ++ .../stage3/source/stage3_hazard_unit.sv | 23 ++++++++++++------- .../stage3/source/stage3_mem_stage.sv | 5 ++-- .../stage3/source/stage3_types_pkg.sv | 4 +++- 6 files changed, 36 insertions(+), 19 deletions(-) diff --git a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh index 4d407e2df..2da9d90e9 100644 --- a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh +++ b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh @@ -35,7 +35,8 @@ interface stage3_hazard_unit_if(); logic i_mem_busy, d_mem_busy, dren, dwen, ret, suppress_data; logic jump, branch, fence_stall; logic mispredict, halt; - word_t pc; + word_t pc_f, pc_e, pc_m; + logic valid_e, valid_m; // f always valid since it's the PC // Control (outputs) logic pc_en, npc_sel; @@ -50,7 +51,7 @@ interface stage3_hazard_unit_if(); //Pipeline Exceptions (inputs) logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m; - word_t epc, badaddr; + word_t badaddr; // Pipeline Tokens logic token_ex; @@ -63,10 +64,11 @@ interface stage3_hazard_unit_if(); input rs1_e, rs2_e, rd_m, reg_write, csr_read, i_mem_busy, d_mem_busy, dren, dwen, ret, - jump, branch, fence_stall, mispredict, halt, pc, + jump, branch, fence_stall, mispredict, halt, pc_f, pc_e, pc_m, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, - epc, badaddr, + badaddr, token_ex, token_mem, rv32c_ready, + valid_e, valid_m, output pc_en, npc_sel, if_ex_flush, ex_mem_flush, if_ex_stall, ex_mem_stall, @@ -75,21 +77,21 @@ interface stage3_hazard_unit_if(); modport fetch ( input pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren, suppress_iren, - output i_mem_busy, rv32c_ready + output i_mem_busy, rv32c_ready, pc_f ); modport execute ( input ex_mem_stall, ex_mem_flush, npc_sel, - output rs1_e, rs2_e, token_ex + output rs1_e, rs2_e, token_ex, pc_e, valid_e ); modport mem ( input ex_mem_stall, ex_mem_flush, suppress_data, output rd_m, reg_write, csr_read, d_mem_busy, dren, dwen, ret, - jump, branch, fence_stall, mispredict, halt, pc, + jump, branch, fence_stall, mispredict, halt, pc_m, valid_m, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, - epc, badaddr, + badaddr, token_mem ); diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index f410a155c..6b16c7edf 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -259,6 +259,8 @@ module stage3_execute_stage ( assign fw_if.rs1_e = rf_if.rs1; assign fw_if.rs2_e = rf_if.rs2; + assign hazard_if.pc_e = fetch_ex_if.fetch_ex_reg.pc; + // TODO: NEW always_ff @(posedge CLK, negedge nRST) begin @@ -271,6 +273,7 @@ module stage3_execute_stage ( // TODO: Handle case of exceptions earlier in the pipe being passed on to handle in the last stage // Single bit control signals -- squash these if we have an exception // Only need to check illegal since it's the only "new" exception we have + ex_mem_if.ex_mem_reg.valid <= fetch_ex_if.fetch_ex_reg.valid; if(!cu_if.illegal_insn) begin ex_mem_if.ex_mem_reg.branch <= cu_if.branch; ex_mem_if.ex_mem_reg.prediction <= fetch_ex_if.fetch_ex_reg.prediction; diff --git a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv index 6a530557e..c18790a86 100644 --- a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv @@ -100,6 +100,7 @@ module stage3_fetch_stage ( assign fault_insn = 1'b0; assign mal_insn = mal_addr; assign badaddr = igen_bus_if.addr; + assign hazard_if.pc_f = pc; //Fetch Execute Pipeline Signals @@ -110,6 +111,7 @@ module stage3_fetch_stage ( else if (hazard_if.if_ex_flush && !hazard_if.if_ex_stall) fetch_ex_if.fetch_ex_reg <= '0; else if (((rv32cif.done | rv32cif.done_earlier) & rv32cif.rv32c_ena) | (!hazard_if.if_ex_stall & !rv32cif.rv32c_ena)) begin + fetch_ex_if.fetch_ex_reg.valid <= 1'b1; fetch_ex_if.fetch_ex_reg.token <= 1'b1; fetch_ex_if.fetch_ex_reg.mal_insn <= mal_insn; fetch_ex_if.fetch_ex_reg.fault_insn <= fault_insn; diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv index 2e52fd709..238b0c031 100644 --- a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -53,6 +53,7 @@ module stage3_hazard_unit ( logic ex_flush_hazard; logic exception; logic intr; + word_t epc; // TODO: RISC-MGMT logic rmgmt_stall; @@ -67,8 +68,8 @@ module stage3_hazard_unit ( assign dmem_access = (hazard_if.dren || hazard_if.dwen); assign branch_jump = hazard_if.jump || (hazard_if.branch && hazard_if.mispredict); - assign wait_for_imem = hazard_if.iren && hazard_if.i_mem_busy && !hazard_if.suppress_data; - assign wait_for_dmem = dmem_access && hazard_if.d_mem_busy && !hazard_if.suppress_iren; + assign wait_for_imem = hazard_if.iren && hazard_if.i_mem_busy && !hazard_if.suppress_iren; + assign wait_for_dmem = dmem_access && hazard_if.d_mem_busy && !hazard_if.suppress_data; assign mem_use_stall = hazard_if.reg_write && cannot_forward && (rs1_match || rs2_match); assign hazard_if.npc_sel = branch_jump; @@ -93,8 +94,13 @@ module stage3_hazard_unit ( assign hazard_if.priv_pc = prv_pipe_if.priv_pc; assign hazard_if.iren = 1'b1; - assign hazard_if.suppress_iren = intr || branch_jump || exception || prv_pipe_if.ret; // prevents a false instruction request from being sent when pipeline flush imminent - assign hazard_if.suppress_data = intr || exception; // suppress data transfer on interrupt/exception. Exception case: prevent read/write of faulting location. Interrupt: make symmetric with exceptions for ease + // TODO: Removed intr as cause of suppression -- is this OK? + assign hazard_if.suppress_iren = branch_jump || exception || prv_pipe_if.ret; // prevents a false instruction request from being sent when pipeline flush imminent + assign hazard_if.suppress_data = exception; // suppress data transfer on interrupt/exception. Exception case: prevent read/write of faulting location. Interrupt: make symmetric with exceptions for ease + + // EPC priority logic + assign epc = hazard_if.valid_m ? hazard_if.pc_m : + (hazard_if.valid_e ? hazard_if.pc_e : hazard_if.pc_f); /* Send Exception notifications to Prv Block */ // TODO: Correct execution of exceptions @@ -114,14 +120,15 @@ module stage3_hazard_unit ( assign prv_pipe_if.ex_rmgmt = rm_if.exception; assign prv_pipe_if.ex_rmgmt_cause = rm_if.ex_cause; - assign prv_pipe_if.epc = hazard_if.epc; + assign prv_pipe_if.epc = epc; assign prv_pipe_if.badaddr = hazard_if.badaddr; /* * Pipeline control signals * - * Control hazard (Ex, Int, Jump, Mispredict): F/E -> Flush, E/M -> Flush + * Control hazard (Exception, Jump, Mispredict): F/E -> Flush, E/M -> Flush + * - Special case: interrupt. Async, don't know where the oldest insn is. Interrupts must assume the memory op will go through, so flush only I/F * Data hazard (unforwardable, load/CSR read): F/E -> Stall, E/M -> Flush * Waiting (i.e. slow dmem access, fence, etc.): * - If fetch stage slow, flush if_ex so in-flight instructions may finish (insert bubbles) @@ -145,9 +152,9 @@ module stage3_hazard_unit ( // || (wait_for_imem && !dmem_access) // ??? //& (~ex_flush_hazard | e_ex_stage) // ??? || rm_if.execute_stall // - || mem_use_stall; // Data hazard -- stall until stall clears (from E/M flush after writeback) + || mem_use_stall; // Data hazard -- stall until dependency clears (from E/M flush after writeback) // TODO: Exceptions - assign hazard_if.ex_mem_stall = wait_for_dmem + assign hazard_if.ex_mem_stall = wait_for_dmem // Second clause ensures we finish memory op on interrupt condition || hazard_if.fence_stall || hazard_if.halt; //|| branch_jump && wait_for_imem; // This can be removed once there is I$. Solves problem where diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index 3b450560c..918c52d1c 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -164,7 +164,7 @@ module stage3_mem_stage( assign hazard_if.csr_read = prv_pipe_if.valid_write; assign hazard_if.token_mem = 0; // TODO: RISC-MGMT assign hazard_if.mispredict = ex_mem_if.ex_mem_reg.prediction ^ ex_mem_if.ex_mem_reg.branch_taken; - assign hazard_if.pc = ex_mem_if.ex_mem_reg.pc; + //assign hazard_if.pc = ex_mem_if.ex_mem_reg.pc; assign halt = ex_mem_if.ex_mem_reg.halt; @@ -194,7 +194,8 @@ module stage3_mem_stage( assign hazard_if.badaddr = (hazard_if.fault_insn || hazard_if.mal_insn) ? ex_mem_if.ex_mem_reg.badaddr : dgen_bus_if.addr; // NEW - assign hazard_if.epc = ex_mem_if.ex_mem_reg.pc; + assign hazard_if.pc_m = ex_mem_if.ex_mem_reg.pc; + assign hazard_if.valid_m = ex_mem_if.ex_mem_reg.valid; // Memory protection (doesn't consider RISC-MGMT) assign prv_pipe_if.dren = ex_mem_if.ex_mem_reg.dren; diff --git a/source_code/pipelines/stage3/source/stage3_types_pkg.sv b/source_code/pipelines/stage3/source/stage3_types_pkg.sv index d0bb29e55..4851a7dd2 100644 --- a/source_code/pipelines/stage3/source/stage3_types_pkg.sv +++ b/source_code/pipelines/stage3/source/stage3_types_pkg.sv @@ -5,7 +5,8 @@ package stage3_types_pkg; import machine_mode_types_1_12_pkg::*; typedef struct packed { - logic token; + logic valid; + logic token; logic mal_insn; logic fault_insn; word_t pc; @@ -26,6 +27,7 @@ package stage3_types_pkg; // TODO: Instructions? typedef struct packed { + logic valid; logic branch; logic prediction; logic branch_taken; From 6fe0e3f902adb2bb59d61e77d9e684237f14a506 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sun, 6 Nov 2022 13:36:21 -0500 Subject: [PATCH 24/47] stage3: RV32C working Adds minor fixes for stalling logic to allow RV32C to work. Includes fixes to forwarding logic that were encountered due to back-to-back execution being possible with RV32C buffer. All RV32I and RV32C tests pass with RV32C enabled and compiling with compression, with the exception of RV32I fence.i, jal, jalr. These are expected failures as they all implicitly assume instructions are aligned to 4 instead of 2 or 4. --- .../stage3/include/stage3_forwarding_unit_if.vh | 8 ++++---- .../pipelines/stage3/source/stage3_execute_stage.sv | 4 ++-- .../pipelines/stage3/source/stage3_fetch_stage.sv | 4 ++-- .../pipelines/stage3/source/stage3_forwarding_unit.sv | 8 ++++---- .../pipelines/stage3/source/stage3_hazard_unit.sv | 2 +- source_code/pipelines/stage3/source/stage3_mem_stage.sv | 9 ++++++--- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh b/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh index d2b6b441d..2dcab72cc 100644 --- a/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh +++ b/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh @@ -3,10 +3,10 @@ interface stage3_forwarding_unit_if(); - logic [4:0] rd_mem; + logic [4:0] rd_m; logic [4:0] rs1_e; logic [4:0] rs2_e; - logic regWEN; + logic reg_write; logic load; logic fwd_rs1; logic fwd_rs2; @@ -18,11 +18,11 @@ interface stage3_forwarding_unit_if(); ); modport mem( - output rd_mem, rd_mem_data, regWEN, load + output rd_m, rd_mem_data, reg_write, load ); modport fw_unit( - input rs1_e, rs2_e, rd_mem, regWEN, load, + input rs1_e, rs2_e, rd_m, reg_write, load, output fwd_rs1, fwd_rs2 ); diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index 6b16c7edf..722dfd2fe 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -139,8 +139,8 @@ module stage3_execute_stage ( //RV32C assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; - assign rv32cif.halt = cu_if.halt; - assign rv32cif.ex_busy = cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; + assign rv32cif.halt = 1'b0; // TODO: Is this signal necessary? Can't get it right on decode of a halt instruction + assign rv32cif.ex_busy = hazard_if.ex_mem_stall; //cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; diff --git a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv index c18790a86..547316f6d 100644 --- a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv @@ -101,6 +101,7 @@ module stage3_fetch_stage ( assign mal_insn = mal_addr; assign badaddr = igen_bus_if.addr; assign hazard_if.pc_f = pc; + assign hazard_if.rv32c_ready = rv32cif.done_earlier && rv32cif.rv32c_ena; // TODO: Is rv32cif.done needed? Seems like it coincides with busy = 0 //Fetch Execute Pipeline Signals @@ -109,8 +110,7 @@ module stage3_fetch_stage ( always_ff @(posedge CLK, negedge nRST) begin if (!nRST) fetch_ex_if.fetch_ex_reg <= '0; else if (hazard_if.if_ex_flush && !hazard_if.if_ex_stall) fetch_ex_if.fetch_ex_reg <= '0; - else if (((rv32cif.done | rv32cif.done_earlier) & rv32cif.rv32c_ena) - | (!hazard_if.if_ex_stall & !rv32cif.rv32c_ena)) begin + else if (!hazard_if.if_ex_stall) begin fetch_ex_if.fetch_ex_reg.valid <= 1'b1; fetch_ex_if.fetch_ex_reg.token <= 1'b1; fetch_ex_if.fetch_ex_reg.mal_insn <= mal_insn; diff --git a/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv b/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv index 9f6e20829..0f5bf141f 100644 --- a/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv @@ -5,10 +5,10 @@ module stage3_forwarding_unit( logic rs1_match, rs2_match; - assign rs1_match = (fw_if.rd_mem != 0) && (fw_if.rs1_e == fw_if.rd_mem); - assign rs2_match = (fw_if.rd_mem != 0) && (fw_if.rs2_e == fw_if.rd_mem); + assign rs1_match = (fw_if.rd_m != 0) && (fw_if.rs1_e == fw_if.rd_m); + assign rs2_match = (fw_if.rd_m != 0) && (fw_if.rs2_e == fw_if.rd_m); - assign fw_if.fwd_rs1 = rs1_match && fw_if.regWEN && !fw_if.load; - assign fw_if.fwd_rs2 = rs2_match && fw_if.regWEN && !fw_if.load; + assign fw_if.fwd_rs1 = rs1_match && fw_if.reg_write && !fw_if.load; + assign fw_if.fwd_rs2 = rs2_match && fw_if.reg_write && !fw_if.load; endmodule diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv index 238b0c031..e93d20b52 100644 --- a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -68,7 +68,7 @@ module stage3_hazard_unit ( assign dmem_access = (hazard_if.dren || hazard_if.dwen); assign branch_jump = hazard_if.jump || (hazard_if.branch && hazard_if.mispredict); - assign wait_for_imem = hazard_if.iren && hazard_if.i_mem_busy && !hazard_if.suppress_iren; + assign wait_for_imem = hazard_if.iren && hazard_if.i_mem_busy && !hazard_if.suppress_iren && !hazard_if.rv32c_ready; // don't wait for imem when rv32c is done early assign wait_for_dmem = dmem_access && hazard_if.d_mem_busy && !hazard_if.suppress_data; assign mem_use_stall = hazard_if.reg_write && cannot_forward && (rs1_match || rs2_match); diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index 918c52d1c..9398585fb 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -148,9 +148,9 @@ module stage3_mem_stage( assign iflushed_next = ifence_pulse ? 1'b0 : cc_if.iflush_done; assign dflushed_next = ifence_pulse ? 1'b0 : cc_if.dflush_done; - /************* - * Hazard Unit - **************/ + /************************ + * Hazard/Forwarding Unit + *************************/ // Note: Some hazard unit signals are assigned below in the CSR section assign hazard_if.d_mem_busy = dgen_bus_if.busy; assign hazard_if.fence_stall = ex_mem_if.ex_mem_reg.ifence && (!dflushed || !iflushed); @@ -167,6 +167,9 @@ module stage3_mem_stage( //assign hazard_if.pc = ex_mem_if.ex_mem_reg.pc; assign halt = ex_mem_if.ex_mem_reg.halt; + assign fw_if.rd_m = ex_mem_if.ex_mem_reg.rd_m; + assign fw_if.rd_mem_data = ex_mem_if.reg_wdata; // forwarded data should be same as what we're outputting to register file + assign fw_if.reg_write = ex_mem_if.reg_write; /****** From 19abd2c80973ddcd8c2a078014a69af7a2b7fa68 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sun, 6 Nov 2022 20:22:30 -0500 Subject: [PATCH 25/47] standard_core: Add test version without memory controller This adds a second version of RISCVBusiness for testing the core that does not have a memory controller. This allows direct access to the buses for testing with different latencies. Currently, it just does immediate latencies to mimic having perfect caches. Additional work would be to add the ability to simulate hits/misses by having a random chance of getting bad latency, independently for I/D streams. --- .../standard_core/RISCVBusiness_no_memory.sv | 179 ++++++++++++++++++ source_code/standard_core/standard_core.core | 4 +- .../standard_core/top_core_no_memory.sv | 98 ++++++++++ 3 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 source_code/standard_core/RISCVBusiness_no_memory.sv create mode 100644 source_code/standard_core/top_core_no_memory.sv diff --git a/source_code/standard_core/RISCVBusiness_no_memory.sv b/source_code/standard_core/RISCVBusiness_no_memory.sv new file mode 100644 index 000000000..63ac60041 --- /dev/null +++ b/source_code/standard_core/RISCVBusiness_no_memory.sv @@ -0,0 +1,179 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: RISCVBusiness.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Top level module for RISCVBusiness +*/ + +`include "generic_bus_if.vh" +`include "component_selection_defines.vh" +`include "risc_mgmt_if.vh" +`include "cache_control_if.vh" +`include "sparce_pipeline_if.vh" +`include "tspp_fetch_execute_if.vh" +`include "tspp_hazard_unit_if.vh" +`include "core_interrupt_if.vh" +`include "rv32c_if.vh" + +module RISCVBusiness_no_memory ( + input logic CLK, + nRST, + output logic wfi, + halt, + core_interrupt_if.core interrupt_if, + generic_bus_if.cpu igen_bus_if, + generic_bus_if.cpu dgen_bus_if + +); + + parameter logic [31:0] RESET_PC = 32'h80000000; + + // Interface instantiations + function logic [31:0] get_x28(); + // verilator public + return pipeline.execute_stage_i.g_rfile_select.rf.registers[28]; + endfunction + + risc_mgmt_if rm_if (); + predictor_pipeline_if predict_if (); + prv_pipeline_if prv_pipe_if (); + cache_control_if cc_if (); + sparce_pipeline_if sparce_if (); + rv32c_if rv32cif (); + + //interface instantiations + tspp_fetch_execute_if fetch_ex_if (); + tspp_hazard_unit_if hazard_if (); + + stage3 #(.RESET_PC(RESET_PC)) pipeline( + .* + ); + + // hardwire cache control signals + assign cc_if.iflush_done = 1'b1; + assign cc_if.iclear_done = 1'b1; + assign cc_if.dflush_done = 1'b1; + assign cc_if.dclear_done = 1'b1; + + // Module Instantiations + /* + pipeline_wrapper pipeline ( + .CLK(CLK), + .nRST(nRST), + .halt(halt), + .igen_bus_if(tspp_icache_gen_bus_if), + .dgen_bus_if(tspp_dcache_gen_bus_if), + .prv_pipe_if(prv_pipe_if), // TODO: Look at the communications between pipeline_wrapper and priv_wrapper + .predict_if(predict_if), + .rm_if(rm_if), + .cc_if(cc_if), + .sparce_if(sparce_if) + ); +*/ + +/* + tspp_fetch_stage #( + .RESET_PC(RESET_PC) + ) fetch_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .igen_bus_if(tspp_icache_gen_bus_if), + .sparce_if(sparce_if), + .rv32cif(rv32cif), + .prv_pipe_if(prv_pipe_if) + ); + + tspp_execute_stage execute_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .dgen_bus_if(tspp_dcache_gen_bus_if), + .prv_pipe_if(prv_pipe_if), + .halt(halt), + .rm_if(rm_if), + .cc_if(cc_if), + .sparce_if(sparce_if), + .rv32cif(rv32cif), + .wfi(wfi) + ); + + tspp_hazard_unit hazard_unit_i ( + .hazard_if(hazard_if), + .prv_pipe_if(prv_pipe_if), + .rm_if(rm_if), + .sparce_if(sparce_if) + ); +*/ + + branch_predictor_wrapper branch_predictor_i ( + .CLK(CLK), + .nRST(nRST), + .predict_if(predict_if) + ); + + priv_wrapper priv_wrapper_i ( + .CLK(CLK), + .nRST(nRST), + .prv_pipe_if(prv_pipe_if), + .interrupt_if + ); + + risc_mgmt_wrapper rmgmt ( + .CLK (CLK), + .nRST (nRST), + .rm_if(rm_if) + ); + + /* + caches_wrapper caches ( + .CLK(CLK), + .nRST(nRST), + .icache_proc_gen_bus_if(tspp_icache_gen_bus_if), + .icache_mem_gen_bus_if(icache_mc_if), + .dcache_proc_gen_bus_if(tspp_dcache_gen_bus_if), + .dcache_mem_gen_bus_if(dcache_mc_if), + .cc_if(cc_if) + ); +*/ + /* + sparce_wrapper sparce_wrapper_i ( + .CLK(CLK), + .nRST(nRST), + .sparce_if(sparce_if) + );*/ + + sparce_disabled sparce_disabled_i ( + .CLK(CLK), + .nRST(nRST), + .sparce_if(sparce_if) + ); + + rv32c_wrapper rv32c ( + .CLK(CLK), + .nRST(nRST), + .rv32cif(rv32cif) + ); + +endmodule diff --git a/source_code/standard_core/standard_core.core b/source_code/standard_core/standard_core.core index 0eef09e76..2c4b8a417 100644 --- a/source_code/standard_core/standard_core.core +++ b/source_code/standard_core/standard_core.core @@ -13,8 +13,8 @@ filesets: - control_unit.sv - jump_calc.sv - rv32i_reg_file.sv - - RISCVBusiness.sv - - top_core.sv + - RISCVBusiness_no_memory.sv + - top_core_no_memory.sv file_type: systemVerilogSource targets: diff --git a/source_code/standard_core/top_core_no_memory.sv b/source_code/standard_core/top_core_no_memory.sv new file mode 100644 index 000000000..cc53ba420 --- /dev/null +++ b/source_code/standard_core/top_core_no_memory.sv @@ -0,0 +1,98 @@ +`include "core_interrupt_if.vh" +`include "generic_bus_if.vh" +`include "component_selection_defines.vh" + +module top_core #( + parameter logic [31:0] RESET_PC = 32'h80000000 +) ( + input CLK, + nRST, + output wfi, + halt, + + // I-bus + input i_busy, + input [31:0] i_rdata, + output i_ren, + output i_wen, + output [3:0] i_byte_en, + output [31:0] i_addr, + output [31:0] i_wdata, + + // D-bus + input d_busy, + input [31:0] d_rdata, + output d_ren, + output d_wen, + output [3:0] d_byte_en, + output [31:0] d_addr, + output [31:0] d_wdata, + + // core_interrupt_if + input ext_int, + ext_int_clear, + input soft_int, + soft_int_clear, + input timer_int, + timer_int_clear +); + + + function [31:0] get_x28; + // verilator public + get_x28 = CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[28]; + endfunction + + bind stage3_mem_stage cpu_tracker cpu_track1 ( + .CLK(CLK), + .wb_stall(wb_stall), + .instr(ex_mem_if.ex_mem_reg.instr), + .pc(ex_mem_if.ex_mem_reg.pc), + .opcode(rv32i_types_pkg::opcode_t'(ex_mem_if.ex_mem_reg.instr[6:0])), + .funct3(funct3), + .funct12(funct12), + .rs1(ex_mem_if.ex_mem_reg.instr[19:15]), + .rs2(ex_mem_if.ex_mem_reg.instr[24:20]), + .rd(ex_mem_if.ex_mem_reg.rd_m), + .imm_S(ex_mem_if.ex_mem_reg.tracker_signals.imm_S), // TODO: Extract constants. Maybe we could pass these in the pipeline and they'd be removed by synthesis? + .imm_I(ex_mem_if.ex_mem_reg.tracker_signals.imm_I), + .imm_U(ex_mem_if.ex_mem_reg.tracker_signals.imm_U), + .imm_UJ(ex_mem_if.ex_mem_reg.tracker_signals.imm_UJ), + .imm_SB(ex_mem_if.ex_mem_reg.tracker_signals.imm_SB), + .instr_30(instr_30) + ); + + + + core_interrupt_if interrupt_if (); + assign interrupt_if.ext_int = ext_int; + assign interrupt_if.ext_int_clear = ext_int_clear; + assign interrupt_if.soft_int = soft_int; + assign interrupt_if.soft_int_clear = soft_int_clear; + assign interrupt_if.timer_int = timer_int; + assign interrupt_if.timer_int_clear = timer_int_clear; + + + generic_bus_if igen_bus_if (); + assign igen_bus_if.busy = i_busy; + assign igen_bus_if.rdata = i_rdata; + assign i_ren = igen_bus_if.ren; + assign i_wen = igen_bus_if.wen; + assign i_byte_en = igen_bus_if.byte_en; + assign i_addr = igen_bus_if.addr; + assign i_wdata = igen_bus_if.wdata; + + generic_bus_if dgen_bus_if (); + assign dgen_bus_if.busy = d_busy; + assign dgen_bus_if.rdata = d_rdata; + assign d_ren = dgen_bus_if.ren; + assign d_wen = dgen_bus_if.wen; + assign d_byte_en = dgen_bus_if.byte_en; + assign d_addr = dgen_bus_if.addr; + assign d_wdata = dgen_bus_if.wdata; + + + + RISCVBusiness_no_memory #(.RESET_PC(RESET_PC)) CORE (.*); + +endmodule From fba6137d8b4016de2c089ed0a36d5abfb9700819 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sun, 6 Nov 2022 20:24:57 -0500 Subject: [PATCH 26/47] stage3: Fix bugs resulting from back-to-back instruction execution This fixes bugs related to hazards occurring only in the case of back-to-back execution of instructions. The fixes were: - Fixing forwarding unit assignments to allow detection of forwarding conditions correctly - Forcing ifence to flush the pipeline and re-fetch in-flight instructions that may no longer be valid - Fix RV32C to obey the pipeline control signals. Previously, it ignored the "pc_en" signal, which led to cases where instructions would be skipped if the first pipeline latch was stalled while RV32C wanted to advance. Remaining items to test/implement: - Flushing for selected CSR writes. Need a list of such instructions, but at minimum this should include the PMP/PMA configuration registers. - Testing variable latencies, instead of only fixed slow/fast latencies --- .../stage3/include/stage3_hazard_unit_if.vh | 10 ++++---- .../stage3/include/stage3_mem_pipe_if.vh | 3 ++- .../stage3/source/stage3_fetch_stage.sv | 8 ++++--- .../stage3/source/stage3_hazard_unit.sv | 23 +++++++++++++++---- .../stage3/source/stage3_mem_stage.sv | 2 ++ source_code/rv32c/fetch_buffer.sv | 2 +- 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh index 2da9d90e9..a0615b0b2 100644 --- a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh +++ b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh @@ -37,12 +37,14 @@ interface stage3_hazard_unit_if(); logic mispredict, halt; word_t pc_f, pc_e, pc_m; logic valid_e, valid_m; // f always valid since it's the PC + logic ifence; // Control (outputs) logic pc_en, npc_sel; logic if_ex_flush, ex_mem_flush; logic if_ex_stall, ex_mem_stall; logic iren, suppress_iren; + logic rollback; // signal for rolling back fetched instructions after instruction in mem stage, for certain CSR and ifence instructions // xTVEC Insertion word_t priv_pc; @@ -66,17 +68,17 @@ interface stage3_hazard_unit_if(); i_mem_busy, d_mem_busy, dren, dwen, ret, jump, branch, fence_stall, mispredict, halt, pc_f, pc_e, pc_m, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, - badaddr, + badaddr, ifence, token_ex, token_mem, rv32c_ready, valid_e, valid_m, output pc_en, npc_sel, if_ex_flush, ex_mem_flush, if_ex_stall, ex_mem_stall, - priv_pc, insert_priv_pc, iren, suppress_iren, suppress_data + priv_pc, insert_priv_pc, iren, suppress_iren, suppress_data, rollback ); modport fetch ( - input pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren, suppress_iren, + input pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren, suppress_iren, rollback, output i_mem_busy, rv32c_ready, pc_f ); @@ -91,7 +93,7 @@ interface stage3_hazard_unit_if(); d_mem_busy, dren, dwen, ret, jump, branch, fence_stall, mispredict, halt, pc_m, valid_m, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, - badaddr, + badaddr, ifence, token_mem ); diff --git a/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh b/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh index 55669b87e..71ea61c16 100644 --- a/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh +++ b/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh @@ -11,9 +11,10 @@ interface stage3_mem_pipe_if(); ex_mem_t ex_mem_reg; word_t brj_addr; word_t reg_wdata; + word_t pc4; // For flush in case of fence_i, CSR, etc. modport fetch( - input brj_addr + input brj_addr, pc4 ); diff --git a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv index 547316f6d..a04bd8ad5 100644 --- a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv @@ -63,24 +63,26 @@ module stage3_fetch_stage ( //RV32C assign rv32cif.inst = igen_bus_if.rdata; assign rv32cif.inst_arrived = hazard_if.if_ex_flush == 0 & hazard_if.if_ex_stall == 0; - assign rv32cif.reset_en = hazard_if.insert_priv_pc | sparce_if.skipping + assign rv32cif.reset_en = hazard_if.insert_priv_pc | hazard_if.rollback | sparce_if.skipping | hazard_if.npc_sel | predict_if.predict_taken; assign rv32cif.pc_update = hazard_if.pc_en; assign rv32cif.reset_pc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (hazard_if.rollback ? mem_pipe_if.pc4 : (sparce_if.skipping ? sparce_if.sparce_target : (hazard_if.npc_sel ? mem_fetch_if.brj_addr : (predict_if.predict_taken ? predict_if.target_addr - : pc4or2))); + : pc4or2)))); assign rv32cif.reset_pc_val = RESET_PC; assign pc4or2 = (rv32cif.rv32c_ena & (rv32cif.result[1:0] != 2'b11)) ? (pc + 2) : (pc + 4); assign predict_if.current_pc = pc; assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (hazard_if.rollback ? mem_pipe_if.pc4 : (sparce_if.skipping ? sparce_if.sparce_target : (hazard_if.npc_sel ? mem_fetch_if.brj_addr : (predict_if.predict_taken ? predict_if.target_addr : rv32cif.rv32c_ena ? rv32cif.nextpc - : pc4or2))); + : pc4or2)))); //Instruction Access logic assign hazard_if.i_mem_busy = igen_bus_if.busy; diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv index e93d20b52..e6f59dffd 100644 --- a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -73,9 +73,7 @@ module stage3_hazard_unit ( assign mem_use_stall = hazard_if.reg_write && cannot_forward && (rs1_match || rs2_match); assign hazard_if.npc_sel = branch_jump; - assign hazard_if.pc_en = (~wait_for_dmem & ~wait_for_imem & ~hazard_if.halt & ~ex_flush_hazard - & ~rmgmt_stall & ~hazard_if.fence_stall) - | branch_jump | prv_pipe_if.insert_pc | prv_pipe_if.ret; + /* Hazards due to Interrupts/Exceptions */ @@ -88,7 +86,7 @@ module stage3_hazard_unit ( assign intr = ~exception & prv_pipe_if.intr; assign prv_pipe_if.pipe_clear = exception | ~(hazard_if.token_ex | rm_if.active_insn); - assign ex_flush_hazard = ((intr | exception) & ~wait_for_dmem) | exception | prv_pipe_if.ret; //TODO: Check this + assign ex_flush_hazard = ((intr || exception) && !wait_for_dmem) || exception || prv_pipe_if.ret || (hazard_if.ifence && !hazard_if.fence_stall); // I-fence must flush to force re-fetch of in-flight instructions. Flush will happen after stallling for cache response. assign hazard_if.insert_priv_pc = prv_pipe_if.insert_pc; assign hazard_if.priv_pc = prv_pipe_if.priv_pc; @@ -98,6 +96,8 @@ module stage3_hazard_unit ( assign hazard_if.suppress_iren = branch_jump || exception || prv_pipe_if.ret; // prevents a false instruction request from being sent when pipeline flush imminent assign hazard_if.suppress_data = exception; // suppress data transfer on interrupt/exception. Exception case: prevent read/write of faulting location. Interrupt: make symmetric with exceptions for ease + assign hazard_if.rollback = (hazard_if.ifence && !hazard_if.fence_stall); // TODO: more cases for CSRs that affect I-fetch (PMA/PMP registers) + // EPC priority logic assign epc = hazard_if.valid_m ? hazard_if.pc_m : (hazard_if.valid_e ? hazard_if.pc_e : hazard_if.pc_f); @@ -136,8 +136,23 @@ module stage3_hazard_unit ( * - If mem stage slow, stall everyone * - Halt - stall everyone * Note: Stall of later stage implies stall of earlier stage. + * PC should not update if: + * - fetch_if is stalling (can't pass instruction on) + * PC should update if: + * - fetch is not stalling + * - there is a forced redirect */ + /*assign hazard_if.pc_en = (~wait_for_dmem & ~wait_for_imem & ~hazard_if.halt & ~ex_flush_hazard + & ~rmgmt_stall & ~hazard_if.fence_stall) + | branch_jump | prv_pipe_if.insert_pc | prv_pipe_if.ret | hazard_if.rollback;*/ + // Unforunately, pc_en is negative logic of stalling + assign hazard_if.pc_en = (!hazard_if.if_ex_stall && !wait_for_imem) // Normal case: next stage free, not waiting for instruction + || branch_jump + || ex_flush_hazard + || prv_pipe_if.insert_pc + || prv_pipe_if.ret; + assign hazard_if.if_ex_flush = ex_flush_hazard // control hazard || branch_jump // control hazard || (wait_for_imem && !hazard_if.ex_mem_stall); // Flush if fetch stage lagging, but ex/mem are moving diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index 9398585fb..64ec303e7 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -153,6 +153,7 @@ module stage3_mem_stage( *************************/ // Note: Some hazard unit signals are assigned below in the CSR section assign hazard_if.d_mem_busy = dgen_bus_if.busy; + assign hazard_if.ifence = ex_mem_if.ex_mem_reg.ifence; assign hazard_if.fence_stall = ex_mem_if.ex_mem_reg.ifence && (!dflushed || !iflushed); assign hazard_if.dren = ex_mem_if.ex_mem_reg.dren; assign hazard_if.dwen = ex_mem_if.ex_mem_reg.dwen; @@ -199,6 +200,7 @@ module stage3_mem_stage( // NEW assign hazard_if.pc_m = ex_mem_if.ex_mem_reg.pc; assign hazard_if.valid_m = ex_mem_if.ex_mem_reg.valid; + assign mem_pipe_if.pc4 = ex_mem_if.ex_mem_reg.pc4; // Memory protection (doesn't consider RISC-MGMT) assign prv_pipe_if.dren = ex_mem_if.ex_mem_reg.dren; diff --git a/source_code/rv32c/fetch_buffer.sv b/source_code/rv32c/fetch_buffer.sv index 4a751cbeb..949661977 100644 --- a/source_code/rv32c/fetch_buffer.sv +++ b/source_code/rv32c/fetch_buffer.sv @@ -41,7 +41,7 @@ module fetch_buffer ( end - assign fb_if.done_earlier = inst_arrived_delay & waitnext_reg & !fb_if.ex_busy; + assign fb_if.done_earlier = inst_arrived_delay & waitnext_reg & !fb_if.ex_busy & fb_if.pc_update; //assign fb_if.done_earlier = 0; // Buffer and PC logic From f141649f7510dad49f30c5c50331f58f9a27a5a0 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sun, 6 Nov 2022 20:30:56 -0500 Subject: [PATCH 27/47] Bugfix: adding .core and TB file for the no_memory version of the TB --- RISCVBusiness.core | 3 +- tb_core_no_memory.cc | 352 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 tb_core_no_memory.cc diff --git a/RISCVBusiness.core b/RISCVBusiness.core index 3fa4c880d..e2c1ee39c 100644 --- a/RISCVBusiness.core +++ b/RISCVBusiness.core @@ -49,7 +49,7 @@ filesets: verilator_tb: files: - - tb_core.cc : {fileType: cppSource} + - tb_core_no_memory.cc : {fileType: cppSource} file_type: systemVerilogSource tb: @@ -91,6 +91,7 @@ targets: verilator: verilator_options: ["-Wno-UNOPTFLAT", "-Wno-SYMRSVDWORD", "-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] + #verilator_options: ["-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] fpga: <<: *default diff --git a/tb_core_no_memory.cc b/tb_core_no_memory.cc new file mode 100644 index 000000000..7a1d05a66 --- /dev/null +++ b/tb_core_no_memory.cc @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "verilated.h" +#include "verilated_fst_c.h" +#include "Vtop_core.h" +#include "Vtop_core_top_core.h" + +#define MTIME_ADDR 0xFFFFFFE0 +#define MTIMEH_ADDR 0xFFFFFFE4 +#define MTIMECMP_ADDR 0xFFFFFFE8 +#define MTIMECMPH_ADDR 0xFFFFFFEC +#define MSIP_ADDR 0xFFFFFFF0 +#define EXT_ADDR_SET 0xFFFFFFF4 +#define EXT_ADDR_CLEAR 0xFFFFFFF8 +#define MAGIC_ADDR 0xFFFFFFFC + +// Inclusive range of memory-mapped peripherals +#define MMIO_RANGE_BEGIN (MTIME_ADDR) +#define MMIO_RANGE_END (MAGIC_ADDR) + +// Predeclare class -- TODO: Move MemoryMap into a header file +class MemoryMap; + +// doubles as mtime counter +vluint64_t sim_time = 0; +Vtop_core *dut_ptr; +VerilatedFstC *trace_ptr; +MemoryMap *mem_ptr; + +/* + * Emulate memory-mapped CSRs + */ +uint64_t mtimecmp = 0; +uint32_t msip = 0; + +// Interrupt signals +bool ext_int = false; + +void signal_handler(int signum) { + std::cout << "Got signal " << signum << std::endl; + dut_ptr->final(); + trace_ptr->close(); + exit(1); +} + +class MemoryMap { +private: + + // NOTE: Assuming 0 for uninitialized memory + // This is because bare-metal tests may not set up bss, + // but the program will nonetheless have a bss section. + const uint32_t c_default_value = 0x00000000; + const char *dumpfile = "memsim.dump"; + std::map mmap; + +protected: + inline uint32_t expand_mask(uint8_t mask) { + uint32_t acc = 0; + for(int i = 0; i < 4; i++) { + auto bit = ((mask & (1 << i)) != 0); + if(bit) { + acc |= (0xFF << (i * 8)); + } + } + + return acc; + } + +public: + + MemoryMap(const char *fname) { + uint32_t address = 0x80000000; + std::ifstream myFile(fname, std::ios::in | std::ios::binary); + if(!myFile) { + std::ostringstream ss; + ss << "Couldn't open " << fname << std::endl; + throw ss.str(); + } + + while(!myFile.eof()) { + uint32_t data; + myFile.read((char *)&data, sizeof(data)); + + mmap.insert(std::make_pair(address, data)); + + address += 4; + } + } + + uint32_t mmio_read(uint32_t addr) { + uint32_t value = 0xBAD1BAD1; + switch(addr) { + case MTIME_ADDR: value = (uint32_t)(sim_time & 0xFFFFFFFF); + break; + case MTIMEH_ADDR: value = (uint32_t)(sim_time >> 32); + break; + case MTIMECMP_ADDR: value = (uint32_t)(mtimecmp & 0xFFFFFFFF); + break; + case MTIMECMPH_ADDR: value = (uint32_t)(mtimecmp >> 32); + break; + case MSIP_ADDR: value = msip; + break; + case EXT_ADDR_SET: value = ext_int; // Marker value for peripheral memory + break; + case EXT_ADDR_CLEAR: value = 0; // Clear 'register' doesn't return a value + break; + case MAGIC_ADDR: value = 0; // TODO: Should this be able to take stdin inputs? Any use? + break; + default: std::cout << "Warning: read of empty MMIO region @ " << std::hex << addr << std::dec << std::endl; + }; + + + return value; + } + + inline static bool is_mmio_region(uint32_t addr) { + return (addr >= MMIO_RANGE_BEGIN && addr <= MMIO_RANGE_END); + } + + void mmio_write(uint32_t addr, uint32_t value, uint8_t mask) { + auto mask_exp = expand_mask(mask); + + if(addr == MTIMECMP_ADDR) { + uint32_t mtimecmp_l = (uint32_t)(mtimecmp & 0xFFFFFFFF); + mtimecmp_l = (value & mask_exp) | (mtimecmp_l & ~mask_exp); + mtimecmp = (mtimecmp & 0xFFFFFFFF00000000) | ((uint64_t)mtimecmp_l); + } else if(addr == MTIMECMPH_ADDR) { + uint32_t mtimecmp_h = (uint32_t)(mtimecmp >> 32); + mtimecmp_h = (value & mask_exp) | (mtimecmp_h & ~mask_exp); + mtimecmp = (mtimecmp & 0xFFFFFFFF) | (((uint64_t)mtimecmp_h) << 32); + } else if(addr == MSIP_ADDR) { + msip = value; + } else if(addr == EXT_ADDR_SET) { + ext_int = true; + } else if(addr == EXT_ADDR_CLEAR) { + ext_int = false; + } else if(addr == MAGIC_ADDR) { + std::cout << (char)(value & 0xFF); + } else { + std::cout << "Warning: write to read-only MMIO region @ " << std::hex << addr << std::dec << std::endl; + } + } + + // TODO: Add simulation for SWI/mtime? + uint32_t read(uint32_t addr) { + auto it = mmap.find(addr); + if(it != mmap.end()) { + return it->second; + } else { + return c_default_value; + } + } + + void write(uint32_t addr, uint32_t value, uint8_t mask) { + // NOTE: For now, assuming that all memory is legally acessible. + auto it = mmap.find(addr); + if(it != mmap.end()) { + auto mask_exp = expand_mask(mask); + it->second = (value & mask_exp) | (it->second & ~mask_exp); + } else { + mmap.insert(std::make_pair(addr, value)); + } + } + + void dump() { + std::ofstream outfile; + outfile.open(dumpfile); + if(!outfile) { + std::ostringstream ss; + ss << "Couldn't open " << dumpfile << std::endl; + throw ss.str(); + } + + // Account for endianness + for(auto p : mmap) { + if(p.second != 0) { + char buf[80]; + snprintf(buf, 80, "%08x : %02x%02x%02x%02x", p.first, + (p.second & 0xFF000000) >> 24, + (p.second & 0x00FF0000) >> 16, + (p.second & 0x0000FF00) >> 8, + p.second & 0x000000FF); + outfile << buf << std::endl; + } + } + } +}; + +void update_interrupt_signals(Vtop_core& dut) { + if(msip & 0x1) { + dut.soft_int = 1; + } else if(dut.soft_int) { + dut.soft_int = 0; + dut.soft_int_clear = 1; + } else if(dut.soft_int_clear) { + dut.soft_int_clear = 0; + } + + if(mtimecmp <= sim_time) { + dut.timer_int = 1; + } else if(dut.timer_int) { + dut.timer_int = 0; + dut.timer_int_clear = 1; + } else if(dut.timer_int_clear) { + dut.timer_int_clear = 0; + } + + if(ext_int) { + dut.ext_int = 1; + } else if(dut.ext_int) { + dut.ext_int = 0; + dut.ext_int_clear = 1; + } else if(dut.ext_int_clear) { + dut.ext_int_clear = 0; + } +} + +void tick(Vtop_core& dut, VerilatedFstC& trace) { + dut.CLK = 0; + dut.eval(); + trace.dump(sim_time); + sim_time++; + dut.CLK = 1; + dut.eval(); + trace.dump(sim_time); + sim_time++; +} + +void reset(Vtop_core& dut, VerilatedFstC& trace) { + // Initialize signals + dut.CLK = 0; + dut.nRST = 0; + dut.ext_int = 0; + dut.ext_int_clear = 0; + dut.soft_int = 0; + dut.soft_int_clear = 0; + dut.timer_int = 0; + dut.timer_int_clear = 0; + dut.i_busy = 1; + dut.i_rdata = 0; + dut.d_busy = 1; + dut.d_rdata = 1; + + tick(dut, trace); + dut.nRST = 0; + tick(dut, trace); + dut.nRST = 1; + tick(dut, trace); +} + +uint32_t do_memory_txn(uint8_t ren, uint8_t wen, uint32_t addr, uint8_t byte_en, uint32_t wdata) { + assert(ren || wen); + if(ren) { + uint32_t addr_in = addr & 0xFFFFFFFC; + if(!MemoryMap::is_mmio_region(addr_in)) { + return mem_ptr->read(addr_in); + } else { + return mem_ptr->mmio_read(addr_in); + } + } else if(wen) { + uint32_t addr_in = addr & 0xFFFFFFFC; + uint32_t value = wdata; + uint8_t mask = byte_en; + if(!MemoryMap::is_mmio_region(addr_in)) { + mem_ptr->write(addr_in, value, mask); + } else { + mem_ptr->mmio_write(addr_in, value, mask); + } + + return 0x0; + } else { + return 0x0; + } +} + + +int main(int argc, char **argv) { + + const char *fname; + + if(argc < 2) { + std::cout << "Warning: No bin file name provided, assuming './meminit.bin' as file location!" << std::endl; + fname = "meminit.bin"; + } else { + fname = argv[1]; + } + + std::cout << "Running no-memory controller TB" << std::endl; + + MemoryMap memory(fname); + + Vtop_core dut; + + Verilated::traceEverOn(true); + VerilatedFstC m_trace; + dut.trace(&m_trace, 5); + m_trace.open("waveform.fst"); + + dut_ptr = &dut; + trace_ptr = &m_trace; + mem_ptr = &memory; + + signal(SIGINT, signal_handler); + + + reset(dut, m_trace); + + + while(!dut.halt && sim_time < 100000) { + // TODO: Variable latency + // Service data first to simulate forwarding from data write to instruction read (required for fence.i w/o caches) + if(dut.d_ren || dut.d_wen) { + dut.d_rdata = do_memory_txn(dut.d_ren, dut.d_wen, dut.d_addr, dut.d_byte_en, dut.d_wdata); + dut.d_busy = 0; + } else { + dut.d_rdata = 0xBAD1BAD1; + dut.d_busy = 1; + } + + if(dut.i_ren || dut.i_wen) { + dut.i_rdata = do_memory_txn(dut.i_ren, dut.i_wen, dut.i_addr, dut.i_byte_en, dut.i_wdata); + dut.i_busy = 0; + } else { + dut.i_rdata = 0xBAD1BAD1; + dut.i_busy = 1; + } + + + tick(dut, m_trace); + update_interrupt_signals(dut); + } + + if(sim_time >= 100000) { + std::cout << "Test TIMED OUT" << std::endl; + } else if(dut.top_core->get_x28() == 1) { + std::cout << "Test PASSED" << std::endl; + } else { + std::cout << "Test FAILED: Test " << dut.top_core->get_x28() << std::endl; + } + m_trace.close(); + memory.dump(); + dut.final(); + + return 0; +} From 7ec493dc009f5935a0527e0a2bf886c6a5f08d9e Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sun, 6 Nov 2022 21:32:34 -0500 Subject: [PATCH 28/47] stage3: Fixes to allow synthesis under Quartus + Genus --- Makefile | 7 ++++++ RISCVBusiness.core | 24 ++++++++++++++++--- source_code/include/rv32i_reg_file_if.vh | 2 +- .../stage3/include/stage3_mem_pipe_if.vh | 2 +- .../stage3/source/stage3_fetch_stage.sv | 4 ++-- .../stage3/source/stage3_mem_stage.sv | 3 ++- source_code/rv32c/fetch_buffer.sv | 4 ++-- source_code/standard_core/standard_core.core | 4 ++-- 8 files changed, 38 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index e64f1536e..436dfb322 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,13 @@ verilate: config @echo "to the build directory created by FuseSoC and using the Makefile there." @echo "------------------------------------------------------------------" +no_mem: config + @fusesoc --cores-root . run --setup --build --build-root rvb_out --target no_mc --tool verilator socet:riscv:RISCVBusiness --make_options='-j' + @echo "------------------------------------------------------------------" + @echo "Build finished, you can run with 'fusesoc run', or by navigating" + @echo "to the build directory created by FuseSoC and using the Makefile there." + @echo "------------------------------------------------------------------" + xcelium: config @fusesoc --cores-root . run --setup --build --build-root rvb_out --target sim --tool xcelium socet:riscv:RISCVBusiness @echo "Build finished, you can run with 'fusesoc run', or by navigating" diff --git a/RISCVBusiness.core b/RISCVBusiness.core index e2c1ee39c..9e0d7e22a 100644 --- a/RISCVBusiness.core +++ b/RISCVBusiness.core @@ -49,8 +49,13 @@ filesets: verilator_tb: files: - - tb_core_no_memory.cc : {fileType: cppSource} - file_type: systemVerilogSource + - tb_core.cc : {fileType: cppSource} + file_type: systemVerilogSource + + no_memory_tb: + files: + - tb_core_no_memory.cc : {fileType: cppSource} + file_type: systemVerilogSource tb: files: @@ -90,9 +95,22 @@ targets: - -t ps verilator: - verilator_options: ["-Wno-UNOPTFLAT", "-Wno-SYMRSVDWORD", "-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] + verilator_options: ["-Wno-SYMRSVDWORD", "-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] #verilator_options: ["-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] + no_mc: + <<: *default + description: Simulate w/TB, no memory controller + default_tool: verilator + filesets_append: + #- trackers + - "tool_verilator? (no_memory_tb)" + toplevel: + - "tool_verilator? (top_core)" + tools: + verilator: + verilator_options: ["-Wno-SYMRSVDWORD", "-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] + fpga: <<: *default filesets_append: diff --git a/source_code/include/rv32i_reg_file_if.vh b/source_code/include/rv32i_reg_file_if.vh index 39fc6cb7e..602221a54 100644 --- a/source_code/include/rv32i_reg_file_if.vh +++ b/source_code/include/rv32i_reg_file_if.vh @@ -39,7 +39,7 @@ interface rv32i_reg_file_if(); ); modport cu ( - output rs1, rs2, rd + output rs1, rs2 ); endinterface diff --git a/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh b/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh index 71ea61c16..2afc4f404 100644 --- a/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh +++ b/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh @@ -25,7 +25,7 @@ interface stage3_mem_pipe_if(); modport mem( input ex_mem_reg, - output brj_addr, reg_wdata, reg_write, rd_m + output brj_addr, reg_wdata, reg_write, rd_m, pc4 ); endinterface diff --git a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv index a04bd8ad5..47c98cd60 100644 --- a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv @@ -67,7 +67,7 @@ module stage3_fetch_stage ( | hazard_if.npc_sel | predict_if.predict_taken; assign rv32cif.pc_update = hazard_if.pc_en; assign rv32cif.reset_pc = hazard_if.insert_priv_pc ? hazard_if.priv_pc - : (hazard_if.rollback ? mem_pipe_if.pc4 + : (hazard_if.rollback ? mem_fetch_if.pc4 : (sparce_if.skipping ? sparce_if.sparce_target : (hazard_if.npc_sel ? mem_fetch_if.brj_addr : (predict_if.predict_taken ? predict_if.target_addr @@ -77,7 +77,7 @@ module stage3_fetch_stage ( assign pc4or2 = (rv32cif.rv32c_ena & (rv32cif.result[1:0] != 2'b11)) ? (pc + 2) : (pc + 4); assign predict_if.current_pc = pc; assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc - : (hazard_if.rollback ? mem_pipe_if.pc4 + : (hazard_if.rollback ? mem_fetch_if.pc4 : (sparce_if.skipping ? sparce_if.sparce_target : (hazard_if.npc_sel ? mem_fetch_if.brj_addr : (predict_if.predict_taken ? predict_if.target_addr diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index 64ec303e7..a019d2236 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -11,6 +11,7 @@ module stage3_mem_stage( input nRST, stage3_mem_pipe_if.mem ex_mem_if, stage3_hazard_unit_if.mem hazard_if, + stage3_forwarding_unit_if.mem fw_if, generic_bus_if.cpu dgen_bus_if, prv_pipeline_if.pipe prv_pipe_if, cache_control_if.pipeline cc_if, @@ -200,7 +201,7 @@ module stage3_mem_stage( // NEW assign hazard_if.pc_m = ex_mem_if.ex_mem_reg.pc; assign hazard_if.valid_m = ex_mem_if.ex_mem_reg.valid; - assign mem_pipe_if.pc4 = ex_mem_if.ex_mem_reg.pc4; + assign ex_mem_if.pc4 = ex_mem_if.ex_mem_reg.pc4; // Memory protection (doesn't consider RISC-MGMT) assign prv_pipe_if.dren = ex_mem_if.ex_mem_reg.dren; diff --git a/source_code/rv32c/fetch_buffer.sv b/source_code/rv32c/fetch_buffer.sv index 949661977..e9bd3f3e7 100644 --- a/source_code/rv32c/fetch_buffer.sv +++ b/source_code/rv32c/fetch_buffer.sv @@ -41,7 +41,7 @@ module fetch_buffer ( end - assign fb_if.done_earlier = inst_arrived_delay & waitnext_reg & !fb_if.ex_busy & fb_if.pc_update; + assign fb_if.done_earlier = inst_arrived_delay & waitnext_reg & !fb_if.ex_busy; //assign fb_if.done_earlier = 0; // Buffer and PC logic @@ -62,7 +62,7 @@ module fetch_buffer ( pc <= fb_if.nextpc; final_inst_store <= final_inst; reset_next <= 1'b1; - end else if (fb_if.inst_arrived | fb_if.done_earlier) begin + end else if ((fb_if.inst_arrived || fb_if.done_earlier) && fb_if.pc_update) begin buffer <= nextbuffer; combine_reg <= combine; waitnext_reg <= waitnext; diff --git a/source_code/standard_core/standard_core.core b/source_code/standard_core/standard_core.core index 2c4b8a417..0eef09e76 100644 --- a/source_code/standard_core/standard_core.core +++ b/source_code/standard_core/standard_core.core @@ -13,8 +13,8 @@ filesets: - control_unit.sv - jump_calc.sv - rv32i_reg_file.sv - - RISCVBusiness_no_memory.sv - - top_core_no_memory.sv + - RISCVBusiness.sv + - top_core.sv file_type: systemVerilogSource targets: From e1b03276fc9f67ea8c0e2278dac2731e38094a8c Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Mon, 14 Nov 2022 22:17:13 -0500 Subject: [PATCH 29/47] stage3: RV32M integration This commit adds RV32M to the stage3 pipeline. This does not use RISC-MGMT, instead opting for a wrapper with enable/disable like RV32C. In discussions with the team, this seems more manageable than trying to fit more complex extensions (that include state) into RISC-MGMT. In the future, RISC-MGMT should be integrated to allow custom instructions. All tests for RV32IMC pass. --- RISCVBusiness.core | 14 +- source_code/include/control_unit_if.vh | 6 +- source_code/packages/riscv_packages.core | 10 +- source_code/packages/rv32m_pkg.sv | 64 +++ .../stage3/include/stage3_hazard_unit_if.vh | 5 +- source_code/pipelines/stage3/source/stage3.sv | 4 +- .../stage3/source/stage3_execute_stage.sv | 384 ++++-------------- .../stage3/source/stage3_hazard_unit.sv | 25 +- .../stage3/source/stage3_mem_stage.sv | 10 +- source_code/rv32m/carry_save_adder.sv | 24 ++ source_code/rv32m/flex_counter_mul.sv | 38 ++ source_code/rv32m/full_adder.sv | 11 + source_code/rv32m/pp_mul32.sv | 347 ++++++++++++++++ source_code/rv32m/radix4_divider.sv | 141 +++++++ source_code/rv32m/rv32m.core | 27 ++ source_code/rv32m/rv32m_decode.sv | 40 ++ source_code/rv32m/rv32m_disabled.sv | 16 + source_code/rv32m/rv32m_enabled.sv | 227 +++++++++++ source_code/rv32m/rv32m_wrapper.sv | 27 ++ source_code/rv32m/shift_add_multiplier.sv | 78 ++++ .../rv32m/shift_test_restore_divider.sv | 92 +++++ source_code/standard_core/RISCVBusiness.sv | 3 +- source_code/standard_core/control_unit.sv | 31 +- source_code/standard_core/top_core.sv | 1 + 24 files changed, 1285 insertions(+), 340 deletions(-) create mode 100644 source_code/packages/rv32m_pkg.sv create mode 100644 source_code/rv32m/carry_save_adder.sv create mode 100644 source_code/rv32m/flex_counter_mul.sv create mode 100644 source_code/rv32m/full_adder.sv create mode 100644 source_code/rv32m/pp_mul32.sv create mode 100644 source_code/rv32m/radix4_divider.sv create mode 100644 source_code/rv32m/rv32m.core create mode 100644 source_code/rv32m/rv32m_decode.sv create mode 100644 source_code/rv32m/rv32m_disabled.sv create mode 100644 source_code/rv32m/rv32m_enabled.sv create mode 100644 source_code/rv32m/rv32m_wrapper.sv create mode 100644 source_code/rv32m/shift_add_multiplier.sv create mode 100644 source_code/rv32m/shift_test_restore_divider.sv diff --git a/RISCVBusiness.core b/RISCVBusiness.core index 9e0d7e22a..52a5bd1a2 100644 --- a/RISCVBusiness.core +++ b/RISCVBusiness.core @@ -12,10 +12,11 @@ filesets: - "socet:riscv:stage3" - "socet:riscv:priv" - "socet:riscv:caches" - - "socet:riscv:risc_mgmt" + #- "socet:riscv:risc_mgmt" - "socet:riscv:riscv_standard" - "socet:riscv:riscv_include" - "socet:riscv:rv32c" + - "socet:riscv:rv32m" files: - source_code/branch_predictors/branch_predictor_wrapper.sv - source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv @@ -42,10 +43,10 @@ filesets: fpga_wrapper: files: - - source_code/fpga/RISCVBusiness_fpga.sv - - source_code/ram/ram_sim_model.sv - - source_code/ram/ram_wrapper.sv - file_type: systemVerilogSource + - source_code/fpga/RISCVBusiness_fpga.sv : {file_type: systemVerilogSource} + - source_code/ram/ram_sim_model.sv : {file_type: systemVerilogSource} + - source_code/ram/ram_wrapper.sv : {file_type: systemVerilogSource} + - rvb.sdc : {file_type: SDC} verilator_tb: files: @@ -54,7 +55,7 @@ filesets: no_memory_tb: files: - - tb_core_no_memory.cc : {fileType: cppSource} + - tb_core_no_memory.cc : {file_type: cppSource} file_type: systemVerilogSource tb: @@ -65,7 +66,6 @@ filesets: - source_code/ram/ram_wrapper.sv file_type: systemVerilogSource - targets: default: &default filesets: diff --git a/source_code/include/control_unit_if.vh b/source_code/include/control_unit_if.vh index d51f4b011..74a443da6 100644 --- a/source_code/include/control_unit_if.vh +++ b/source_code/include/control_unit_if.vh @@ -30,6 +30,7 @@ interface control_unit_if; import alu_types_pkg::*; import rv32i_types_pkg::*; import machine_mode_types_1_12_pkg::*; + import rv32m_pkg::*; logic dwen, dren, j_sel, branch, jump, ex_pc_sel, imm_shamt_sel, halt, wen, ifence, wfi; aluop_t alu_op; @@ -51,6 +52,9 @@ interface control_unit_if; csr_addr_t csr_addr; logic [4:0] zimm; + // Extension control signals + rv32m_decode_t rv32m_control; + modport control_unit( input instr, output dwen, dren, j_sel, branch, jump, ex_pc_sel, alu_a_sel, @@ -58,7 +62,7 @@ interface control_unit_if; imm_I, imm_S, imm_SB, imm_UJ, imm_U, imm_shamt_sel, alu_op, opcode, halt, wen, fault_insn, illegal_insn, ret_insn, breakpoint, ecall_insn, csr_swap, csr_set, csr_clr, csr_imm, csr_rw_valid, - csr_addr, zimm, ifence, wfi, rd + csr_addr, zimm, ifence, wfi, rd, rv32m_control ); endinterface diff --git a/source_code/packages/riscv_packages.core b/source_code/packages/riscv_packages.core index d38035e20..275e25eae 100644 --- a/source_code/packages/riscv_packages.core +++ b/source_code/packages/riscv_packages.core @@ -10,10 +10,11 @@ filesets: - machine_mode_types_1_12_pkg.sv - pma_types_1_12_pkg.sv - pmp_types_1_12_pkg.sv - - risc_mgmt/crc32_pkg.sv - - risc_mgmt/template_pkg.sv - - risc_mgmt/test_pkg.sv - - risc_mgmt/rv32m_pkg.sv + - rv32m_pkg.sv + #- risc_mgmt/crc32_pkg.sv + #- risc_mgmt/template_pkg.sv + #- risc_mgmt/test_pkg.sv + #- risc_mgmt/rv32m_pkg.sv file_type: systemVerilogSource targets: @@ -29,4 +30,3 @@ targets: tools: veriblelint: verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search'] - diff --git a/source_code/packages/rv32m_pkg.sv b/source_code/packages/rv32m_pkg.sv new file mode 100644 index 000000000..070cb6dbd --- /dev/null +++ b/source_code/packages/rv32m_pkg.sv @@ -0,0 +1,64 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32m_pkg.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/07/2017 +* Description: Types for the RV32M standard extension +*/ + +`ifndef RV32M_PKG_SV +`define RV32M_PKG_SV + +package rv32m_pkg; + + localparam logic [6:0] RV32M_OPCODE = 7'b0110011; + localparam logic [6:0] RV32M_OPCODE_MINOR = 7'b0000001; + + typedef struct packed { + logic [6:0] opcode_minor; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct; + logic [4:0] rd; + logic [6:0] opcode_major; + } rv32m_insn_t; + + // Equivalent to ALUOP for integer, decode operation locally to the FU + // This should be direct-cast of the funct3 field of insn + typedef enum logic [2:0] { + MUL = 3'b000, + MULH = 3'b001, + MULHSU = 3'b010, + MULHU = 3'b011, + DIV = 3'b100, + DIVU = 3'b101, + REM = 3'b110, + REMU = 3'b111 + } rv32m_op_t; + + // RV32M decoder: output + typedef struct packed { + logic select; + rv32m_op_t op; + } rv32m_decode_t; + + +endpackage + +`endif //RV32M_PKG_SV diff --git a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh index a0615b0b2..ab4b34648 100644 --- a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh +++ b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh @@ -38,6 +38,7 @@ interface stage3_hazard_unit_if(); word_t pc_f, pc_e, pc_m; logic valid_e, valid_m; // f always valid since it's the PC logic ifence; + logic ex_busy; // Control (outputs) logic pc_en, npc_sel; @@ -70,7 +71,7 @@ interface stage3_hazard_unit_if(); fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, badaddr, ifence, token_ex, token_mem, rv32c_ready, - valid_e, valid_m, + valid_e, valid_m, ex_busy, output pc_en, npc_sel, if_ex_flush, ex_mem_flush, if_ex_stall, ex_mem_stall, @@ -84,7 +85,7 @@ interface stage3_hazard_unit_if(); modport execute ( input ex_mem_stall, ex_mem_flush, npc_sel, - output rs1_e, rs2_e, token_ex, pc_e, valid_e + output rs1_e, rs2_e, token_ex, pc_e, valid_e, ex_busy ); modport mem ( diff --git a/source_code/pipelines/stage3/source/stage3.sv b/source_code/pipelines/stage3/source/stage3.sv index 703f10452..3b57adc85 100644 --- a/source_code/pipelines/stage3/source/stage3.sv +++ b/source_code/pipelines/stage3/source/stage3.sv @@ -27,7 +27,7 @@ `include "predictor_pipeline_if.vh" `include "generic_bus_if.vh" `include "prv_pipeline_if.vh" -`include "risc_mgmt_if.vh" +//`include "risc_mgmt_if.vh" `include "cache_control_if.vh" `include "sparce_pipeline_if.vh" `include "rv32c_if.vh" @@ -43,7 +43,7 @@ module stage3 #( generic_bus_if.cpu dgen_bus_if, prv_pipeline_if prv_pipe_if, predictor_pipeline_if predict_if, - risc_mgmt_if rm_if, + //risc_mgmt_if rm_if, cache_control_if cc_if, sparce_pipeline_if sparce_if, rv32c_if rv32cif diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index 722dfd2fe..437f4cb79 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -30,7 +30,7 @@ `include "rv32i_reg_file_if.vh" `include "alu_if.vh" //`include "prv_pipeline_if.vh" -`include "risc_mgmt_if.vh" +//`include "risc_mgmt_if.vh" `include "rv32c_if.vh" module stage3_execute_stage ( @@ -41,7 +41,7 @@ module stage3_execute_stage ( stage3_hazard_unit_if.execute hazard_if, stage3_forwarding_unit_if.execute fw_if, //prv_pipeline_if.pipe prv_pipe_if, - risc_mgmt_if.ts_execute rm_if, + //risc_mgmt_if.ts_execute rm_if, sparce_pipeline_if.pipe_execute sparce_if, rv32c_if.execute rv32cif ); @@ -57,15 +57,31 @@ module stage3_execute_stage ( jump_calc_if jump_if (); branch_res_if branch_if (); - // Module instantiations + /********************** + * Decode/Register Read + ***********************/ + + // RV32C inputs + assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; + assign rv32cif.halt = 1'b0; // TODO: Is this signal necessary? Can't get it right on decode of a halt instruction + assign rv32cif.ex_busy = hazard_if.ex_mem_stall; //cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; + assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + //assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + + // Control unit, inputs are post-decompression control_unit cu ( .cu_if(cu_if), .rf_if(rf_if), - .rmgmt_rsel_s_0(rm_if.rsel_s_0), - .rmgmt_rsel_s_1(rm_if.rsel_s_1), - .rmgmt_rsel_d(rm_if.rsel_d), - .rmgmt_req_reg_r(rm_if.req_reg_r), - .rmgmt_req_reg_w(rm_if.req_reg_w) + .rmgmt_rsel_s_0(), + .rmgmt_rsel_s_1(), + .rmgmt_rsel_d(), + .rmgmt_req_reg_r(), + .rmgmt_req_reg_w() + //.rmgmt_rsel_s_0(rm_if.rsel_s_0), + //.rmgmt_rsel_s_1(rm_if.rsel_s_1), + //.rmgmt_rsel_d(rm_if.rsel_d), + //.rmgmt_req_reg_r(rm_if.req_reg_r), + //.rmgmt_req_reg_w(rm_if.req_reg_w) ); assign wfi = cu_if.wfi; //Added by rkannank @@ -86,76 +102,47 @@ module stage3_execute_stage ( end endgenerate - // Forwarding -- replace rs1/rs2 with post-forwarded versions - word_t rs1_post_fwd, rs2_post_fwd; - // TODO: Make sure rs2_post_fwd used everywhere rs2 data is meant to be used! - assign rs1_post_fwd = fw_if.fwd_rs1 ? fw_if.rd_mem_data : rf_if.rs1_data; - assign rs2_post_fwd = fw_if.fwd_rs2 ? fw_if.rd_mem_data : rf_if.rs2_data; - + + /****************** + * Functional Units + *******************/ + logic rv32m_busy; + word_t rv32m_out; + word_t ex_out; + alu alu (.*); jump_calc jump_calc (.*); - branch_res branch_res (.br_if(branch_if)); - - /* - word_t store_swapped; - endian_swapper store_swap ( - .word_in (rf_if.rs2_data), - .word_out(store_swapped) - ); - - word_t dload_ext; - logic [3:0] byte_en, byte_en_temp, byte_en_standard; - dmem_extender dmem_ext ( - .dmem_in (dgen_bus_if.rdata), - .load_type(cu_if.load_type), - .byte_en (byte_en), - .ext_out (dload_ext) + + rv32m_wrapper RV32M_FU ( + .CLK, + .nRST, + .rv32m_start(cu_if.rv32m_control.select), + .operation(cu_if.rv32m_control.op), // TODO: Better way? + .rv32m_a(rs1_post_fwd), // All RV32M are reg-reg, so just feed post-fwd regs + .rv32m_b(rs2_post_fwd), + .rv32m_busy, + .rv32m_out ); - */ - - /******************************************************* - * MISC RISC-MGMT Logic - *******************************************************/ - - //assign rm_if.rdata_s_0 = rf_if.rs1_data; - //assign rm_if.rdata_s_1 = rf_if.rs2_data; - - - /******************************************************** - *** Choose the Endianness Coming into the processor - *******************************************************/ - /* - generate - if (BUS_ENDIANNESS == "big") begin : g_data_bus_be - assign byte_en = byte_en_temp; - end else if (BUS_ENDIANNESS == "little") begin : g_data_bus_le - assign byte_en = cu_if.dren ? byte_en_temp : - {byte_en_temp[0], byte_en_temp[1], - byte_en_temp[2], byte_en_temp[3]}; - end - endgenerate - */ - - //RV32C - assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; - assign rv32cif.halt = 1'b0; // TODO: Is this signal necessary? Can't get it right on decode of a halt instruction - assign rv32cif.ex_busy = hazard_if.ex_mem_stall; //cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; - assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; - assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + // Forwarding + // These rs*_post_fwd values should be used in place of rs1/rs2 anywhere they are used + word_t rs1_post_fwd, rs2_post_fwd; + assign rs1_post_fwd = fw_if.fwd_rs1 ? fw_if.rd_mem_data : rf_if.rs1_data; + assign rs2_post_fwd = fw_if.fwd_rs2 ? fw_if.rd_mem_data : rf_if.rs2_data; + - /******************************************************* - *** Sign Extensions - *******************************************************/ + /****************** + * Sign Extensions + ******************/ word_t imm_I_ext, imm_S_ext, imm_UJ_ext; assign imm_I_ext = {{20{cu_if.imm_I[11]}}, cu_if.imm_I}; assign imm_UJ_ext = {{11{cu_if.imm_UJ[20]}}, cu_if.imm_UJ}; assign imm_S_ext = {{20{cu_if.imm_S[11]}}, cu_if.imm_S}; - /******************************************************* - *** Jump Target Calculator and Associated Logic - *******************************************************/ + /********************************************** + * Jump Target Calculator and Associated Logic + **********************************************/ word_t jump_addr /* verilator isolate_assignments */; always_comb begin if (cu_if.j_sel) begin @@ -169,9 +156,9 @@ module stage3_execute_stage ( end end - /******************************************************* - *** ALU and Associated Logic - *******************************************************/ + /***** + * ALU + *****/ word_t imm_or_shamt; assign imm_or_shamt = (cu_if.imm_shamt_sel == 1'b1) ? cu_if.shamt : imm_I_ext; assign alu_if.aluop = cu_if.alu_op; @@ -195,38 +182,22 @@ module stage3_execute_stage ( endcase end - // TODO: Fix this for correct writeback! Mem stage needs mux this for us + + // FU output mux -- feeds into pipeline register + // Add to this when more FUs are added + // TODO: Make this nicer, with enum for FU selection + assign ex_out = (cu_if.rv32m_control.select) ? rv32m_out : alu_if.port_out; + + /************************* + * Register File Writeback + *************************/ assign rf_if.w_data = ex_mem_if.reg_wdata; assign rf_if.rd = ex_mem_if.rd_m; - /* - always_comb begin - if (rm_if.req_reg_w) begin - rf_if.w_data = rm_if.reg_wdata; - end else begin - - case (cu_if.w_sel) - 3'd0: rf_if.w_data = dload_ext; - 3'd1: rf_if.w_data = fetch_ex_if.fetch_ex_reg.pc4; - 3'd2: rf_if.w_data = cu_if.imm_U; - 3'd3: rf_if.w_data = alu_if.port_out; - 3'd4: rf_if.w_data = prv_pipe_if.rdata; - default: rf_if.w_data = '0; - endcase - end - end - */ - - // TODO: What is the real condition for writeback? - // Does checking the stall actually matter? (value is coming from register, no path from register update->register input now) - /*assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) - & (~hazard_if.ex_mem_stall | hazard_if.npc_sel | rv32cif.done_earlier) - & ~(cu_if.dren & mal_addr); - */ assign rf_if.wen = ex_mem_if.reg_write && !hazard_if.ex_mem_stall; // TODO: The second signal only matters for some miniscule power reduction by not writing each cycle. This is correct with only the wen signal due to no loop from reg read to reg write - /******************************************************* - *** Branch Target Resolution and Associated Logic - *******************************************************/ - + + /*********************************************** + * Branch Target Resolution and Associated Logic + ***********************************************/ word_t resolved_addr; logic branch_taken; word_t branch_addr; @@ -239,14 +210,14 @@ module stage3_execute_stage ( assign branch_if.branch_type = cu_if.branch_type; // Mux resource based on if RISC-MGMT is trying to access it - assign branch_taken = rm_if.req_br_j ? rm_if.branch_jump : branch_if.branch_taken; - assign branch_addr = rm_if.req_br_j ? rm_if.br_j_addr : branch_if.branch_addr; + assign branch_taken = branch_if.branch_taken;//rm_if.req_br_j ? rm_if.branch_jump : branch_if.branch_taken; + assign branch_addr = branch_if.branch_addr;//rm_if.req_br_j ? rm_if.br_j_addr : branch_if.branch_addr; //assign rm_if.pc = fetch_ex_if.fetch_ex_reg.pc; assign resolved_addr = branch_if.branch_taken ? branch_addr : fetch_ex_if.fetch_ex_reg.pc4; - - assign brj_addr = ((cu_if.ex_pc_sel == 1'b1) && ~rm_if.req_br_j) ? - jump_addr : resolved_addr; + assign brj_addr = cu_if.ex_pc_sel ? jump_addr : resolved_addr; + //assign brj_addr = ((cu_if.ex_pc_sel == 1'b1) && ~rm_if.req_br_j) ? + // jump_addr : resolved_addr; //assign hazard_if.mispredict = fetch_ex_if.fetch_ex_reg.prediction ^ branch_taken; @@ -260,6 +231,7 @@ module stage3_execute_stage ( assign fw_if.rs2_e = rf_if.rs2; assign hazard_if.pc_e = fetch_ex_if.fetch_ex_reg.pc; + assign hazard_if.ex_busy = rv32m_busy; // Add & conditions here for other FUs that can stall // TODO: NEW @@ -307,7 +279,7 @@ module stage3_execute_stage ( // Word sized members ex_mem_if.ex_mem_reg.brj_addr <= brj_addr; - ex_mem_if.ex_mem_reg.port_out <= alu_if.port_out; + ex_mem_if.ex_mem_reg.port_out <= ex_out; ex_mem_if.ex_mem_reg.rs1_data <= rs1_post_fwd; ex_mem_if.ex_mem_reg.rs2_data <= rs2_post_fwd; ex_mem_if.ex_mem_reg.instr <= cu_if.instr; @@ -330,212 +302,14 @@ module stage3_execute_stage ( end end - //// - // HERE BELOW WAS MOVED TO MEMORY STAGE - //// - - /******************************************************* - *** Data Ram Interface Logic - *******************************************************/ - /* - logic [1:0] byte_offset; - - // RISC-MGMT connection - assign rm_if.mem_load = dgen_bus_if.rdata; - - assign dgen_bus_if.ren = rm_if.req_mem ? rm_if.mem_ren : cu_if.dren & ~mal_addr & ~prv_pipe_if.prot_fault_l; - assign dgen_bus_if.wen = rm_if.req_mem ? rm_if.mem_wen : cu_if.dwen & ~mal_addr & ~prv_pipe_if.prot_fault_s; - assign byte_en_temp = rm_if.req_mem ? rm_if.mem_byte_en : byte_en_standard; - assign dgen_bus_if.byte_en = byte_en; - assign dgen_bus_if.addr = rm_if.req_mem ? rm_if.mem_addr : alu_if.port_out; - assign hazard_if.d_mem_busy = dgen_bus_if.busy; - assign byte_offset = alu_if.port_out[1:0]; - - always_comb begin - dgen_bus_if.wdata = '0; - if (rm_if.req_mem) dgen_bus_if.wdata = rm_if.mem_store; - else begin - case (cu_if.load_type) // load_type can be used for store_type as well - LB: dgen_bus_if.wdata = {4{rf_if.rs2_data[7:0]}}; - LH: dgen_bus_if.wdata = {2{rf_if.rs2_data[15:0]}}; - LW: dgen_bus_if.wdata = rf_if.rs2_data; - endcase - end - end - - - // Assign byte_en based on load type - // funct3 for loads and stores are the same bit positions - // byte_en is valid for both loads and stores - always_comb begin - unique case (cu_if.load_type) - LB: begin - unique case (byte_offset) - 2'b00: byte_en_standard = 4'b0001; - 2'b01: byte_en_standard = 4'b0010; - 2'b10: byte_en_standard = 4'b0100; - 2'b11: byte_en_standard = 4'b1000; - default: byte_en_standard = 4'b0000; - endcase - end - LBU: begin - unique case (byte_offset) - 2'b00: byte_en_standard = 4'b0001; - 2'b01: byte_en_standard = 4'b0010; - 2'b10: byte_en_standard = 4'b0100; - 2'b11: byte_en_standard = 4'b1000; - default: byte_en_standard = 4'b0000; - endcase - end - LH: begin - unique case (byte_offset) - 2'b00: byte_en_standard = 4'b0011; - 2'b10: byte_en_standard = 4'b1100; - default: byte_en_standard = 4'b0000; - endcase - end - LHU: begin - unique case (byte_offset) - 2'b00: byte_en_standard = 4'b0011; - 2'b10: byte_en_standard = 4'b1100; - default: byte_en_standard = 4'b0000; - endcase - end - LW: byte_en_standard = 4'b1111; - default: byte_en_standard = 4'b0000; - endcase - end - */ - - // Fence instructions - - // posedge detector for ifence - // subsequent ifences will have same effect as a single fence - /* - logic ifence_reg; - logic ifence_pulse; - - always_ff @(posedge CLK, negedge nRST) begin - if (~nRST) ifence_reg <= 1'b0; - else ifence_reg <= cu_if.ifence; - end - - assign ifence_pulse = cu_if.ifence && ~ifence_reg; - assign cc_if.icache_flush = ifence_pulse; - assign cc_if.icache_clear = 1'b0; - assign cc_if.dcache_flush = ifence_pulse; - assign cc_if.dcache_clear = 1'b0; - - //regs to detect flush completion - logic dflushed, iflushed; - - always_ff @(posedge CLK, negedge nRST) begin - if (~nRST) iflushed <= 1'b1; - else if (ifence_pulse) iflushed <= 1'b0; - else if (cc_if.iflush_done) iflushed <= 1'b1; - end - - always_ff @(posedge CLK, negedge nRST) begin - if (~nRST) dflushed <= 1'b1; - else if (ifence_pulse) dflushed <= 1'b0; - else if (cc_if.dflush_done) dflushed <= 1'b1; - end - - assign hazard_if.fence_stall = cu_if.ifence && (~dflushed || ~iflushed); - */ - - /******************************************************* - *** Hazard Unit Interface Logic - *******************************************************/ - /*assign hazard_if.dren = cu_if.dren; - assign hazard_if.dwen = cu_if.dwen; - assign hazard_if.jump = cu_if.jump; - assign hazard_if.branch = cu_if.branch; - assign hazard_if.halt = halt; - - always_ff @(posedge CLK, negedge nRST) begin - if (~nRST) halt <= 1'b0; - else if (cu_if.halt) halt <= cu_if.halt; - end*/ - - /******************************************************* - *** CSR / Priv Interface Logic - *******************************************************/ - /* - assign prv_pipe_if.swap = cu_if.csr_swap; - assign prv_pipe_if.clr = cu_if.csr_clr; - assign prv_pipe_if.set = cu_if.csr_set; - assign prv_pipe_if.wdata = cu_if.csr_imm ? {27'h0, cu_if.zimm} : rf_if.rs1_data; - assign prv_pipe_if.csr_addr = cu_if.csr_addr; - assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr | - prv_pipe_if.set) & ~hazard_if.if_ex_stall; - assign prv_pipe_if.instr = (cu_if.instr != '0); - - - - - - //Send exceptions to Hazard Unit - assign hazard_if.fault_l = 1'b0; - assign hazard_if.mal_l = cu_if.dren & mal_addr; - assign hazard_if.fault_s = 1'b0; - assign hazard_if.mal_s = cu_if.dwen & mal_addr; - assign hazard_if.breakpoint = cu_if.breakpoint; - assign hazard_if.env_m = cu_if.ecall_insn; - assign hazard_if.ret = cu_if.ret_insn; - assign hazard_if.badaddr_e = dgen_bus_if.addr; - - */ - // TODO: Illegal instruction checks. Maybe pass these on to next stage? - //assign hazard_if.illegal_insn = (cu_if.illegal_insn & ~rm_if.ex_token) - // | prv_pipe_if.invalid_csr; - //assign hazard_if.illegal_insn = 1'b0; - - //assign hazard_if.epc_e = fetch_ex_if.fetch_ex_reg.pc; - //assign hazard_if.token_ex = fetch_ex_if.fetch_ex_reg.token; - - // setup signals for memory protection - // not considering risc-mgmt here TODO - /* - assign prv_pipe_if.dren = cu_if.dren & ~mal_addr; - assign prv_pipe_if.dwen = cu_if.dwen & ~mal_addr; - assign prv_pipe_if.daddr = alu_if.port_out; - assign prv_pipe_if.d_acc_width = WordAcc; - */ - - /********************************************************* - *** Branch Predictor Logic - *********************************************************/ - /* - assign predict_if.update_predictor = cu_if.branch; - assign predict_if.prediction = fetch_ex_if.fetch_ex_reg.prediction; - assign predict_if.branch_result = branch_if.branch_taken; - //predict_if.update_addr = ; - */ - /********************************************************* *** SparCE Module Logic *********************************************************/ - assign sparce_if.wb_data = rf_if.w_data; + /*assign sparce_if.wb_data = rf_if.w_data; assign sparce_if.wb_en = rf_if.wen; assign sparce_if.sasa_data = rf_if.rs2_data; assign sparce_if.sasa_addr = alu_if.port_out; assign sparce_if.sasa_wen = cu_if.dwen; - assign sparce_if.rd = rf_if.rd; - - /********************************************************* - *** Signals for Bind Tracking - Read-Only, These don't affect execution - *********************************************************/ - /* - logic wb_stall; - logic [2:0] funct3; - logic [11:0] funct12; - logic instr_30; - - assign wb_stall = hazard_if.if_ex_stall & ~hazard_if.jump & ~hazard_if.branch; - assign funct3 = cu_if.instr[14:12]; - assign funct12 = cu_if.instr[31:20]; - assign instr_30 = cu_if.instr[30]; - */ + assign sparce_if.rd = rf_if.rd;*/ endmodule diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv index e6f59dffd..4e83b1a79 100644 --- a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -25,13 +25,13 @@ `include "stage3_hazard_unit_if.vh" `include "prv_pipeline_if.vh" -`include "risc_mgmt_if.vh" +//`include "risc_mgmt_if.vh" module stage3_hazard_unit ( stage3_hazard_unit_if.hazard_unit hazard_if, - prv_pipeline_if.hazard prv_pipe_if, - risc_mgmt_if.ts_hazard rm_if, - sparce_pipeline_if.hazard sparce_if + prv_pipeline_if.hazard prv_pipe_if + //risc_mgmt_if.ts_hazard rm_if, + //sparce_pipeline_if.hazard sparce_if ); import alu_types_pkg::*; import rv32i_types_pkg::*; @@ -56,10 +56,10 @@ module stage3_hazard_unit ( word_t epc; // TODO: RISC-MGMT - logic rmgmt_stall; + //logic rmgmt_stall; - assign rm_if.if_ex_enable = ~hazard_if.if_ex_stall; - assign rmgmt_stall = rm_if.memory_stall | rm_if.execute_stall; + //assign rm_if.if_ex_enable = ~hazard_if.if_ex_stall; + //assign rmgmt_stall = rm_if.memory_stall | rm_if.execute_stall; // Hazard detection assign rs1_match = (hazard_if.rs1_e == hazard_if.rd_m) && (hazard_if.rd_m != 0); @@ -85,7 +85,7 @@ module stage3_hazard_unit ( assign intr = ~exception & prv_pipe_if.intr; - assign prv_pipe_if.pipe_clear = exception | ~(hazard_if.token_ex | rm_if.active_insn); + assign prv_pipe_if.pipe_clear = 1'b1; // TODO: What is this for?//exception; //| ~(hazard_if.token_ex | rm_if.active_insn); assign ex_flush_hazard = ((intr || exception) && !wait_for_dmem) || exception || prv_pipe_if.ret || (hazard_if.ifence && !hazard_if.fence_stall); // I-fence must flush to force re-fetch of in-flight instructions. Flush will happen after stallling for cache response. assign hazard_if.insert_priv_pc = prv_pipe_if.insert_pc; @@ -117,9 +117,9 @@ module stage3_hazard_unit ( assign prv_pipe_if.mal_s = hazard_if.mal_s; assign prv_pipe_if.breakpoint = hazard_if.breakpoint; assign prv_pipe_if.env_m = hazard_if.env_m; - assign prv_pipe_if.ex_rmgmt = rm_if.exception; + assign prv_pipe_if.ex_rmgmt = 1'b0;//rm_if.exception; - assign prv_pipe_if.ex_rmgmt_cause = rm_if.ex_cause; + assign prv_pipe_if.ex_rmgmt_cause = '0;//rm_if.ex_cause; assign prv_pipe_if.epc = epc; assign prv_pipe_if.badaddr = hazard_if.badaddr; @@ -166,7 +166,8 @@ module stage3_hazard_unit ( assign hazard_if.if_ex_stall = hazard_if.ex_mem_stall // Stall this stage if next stage is stalled // || (wait_for_imem && !dmem_access) // ??? //& (~ex_flush_hazard | e_ex_stage) // ??? - || rm_if.execute_stall // + //|| rm_if.execute_stall // + || hazard_if.ex_busy // Waiting for extension || mem_use_stall; // Data hazard -- stall until dependency clears (from E/M flush after writeback) // TODO: Exceptions assign hazard_if.ex_mem_stall = wait_for_dmem // Second clause ensures we finish memory op on interrupt condition @@ -179,6 +180,6 @@ module stage3_hazard_unit ( /********************************************************* *** SparCE Module Logic *********************************************************/ - assign sparce_if.if_ex_enable = rm_if.if_ex_enable; + //assign sparce_if.if_ex_enable = rm_if.if_ex_enable; endmodule diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index a019d2236..871958483 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -170,7 +170,6 @@ module stage3_mem_stage( assign halt = ex_mem_if.ex_mem_reg.halt; assign fw_if.rd_m = ex_mem_if.ex_mem_reg.rd_m; - assign fw_if.rd_mem_data = ex_mem_if.reg_wdata; // forwarded data should be same as what we're outputting to register file assign fw_if.reg_write = ex_mem_if.reg_write; @@ -228,6 +227,15 @@ module stage3_mem_stage( 3'd4: ex_mem_if.reg_wdata = prv_pipe_if.rdata; default: ex_mem_if.reg_wdata = '0; endcase + + // Forwarding unit + case (ex_mem_if.ex_mem_reg.w_sel) + 3'd1: fw_if.rd_mem_data = ex_mem_if.ex_mem_reg.pc4; + 3'd2: fw_if.rd_mem_data = ex_mem_if.ex_mem_reg.imm_U; + 3'd3: fw_if.rd_mem_data = ex_mem_if.ex_mem_reg.port_out; + 3'd4: fw_if.rd_mem_data = prv_pipe_if.rdata; + default: fw_if.rd_mem_data = '0; + endcase end /************** diff --git a/source_code/rv32m/carry_save_adder.sv b/source_code/rv32m/carry_save_adder.sv new file mode 100644 index 000000000..f1cfe0379 --- /dev/null +++ b/source_code/rv32m/carry_save_adder.sv @@ -0,0 +1,24 @@ +module carry_save_adder #( + parameter int BIT_WIDTH = 32 +) ( + input logic [(BIT_WIDTH-1):0] x, + input logic [(BIT_WIDTH-1):0] y, + input logic [(BIT_WIDTH-1):0] z, + output logic [(BIT_WIDTH-1):0] cout, + output logic [(BIT_WIDTH-1):0] sum +); + genvar i; + logic [(BIT_WIDTH-1):0] c; + generate + for (i = 0; i < BIT_WIDTH; i = i + 1) begin : g_mul_csa + full_adder FA ( + .x(x[i]), + .y(y[i]), + .cin(z[i]), + .cout(c[i]), + .sum(sum[i]) + ); + end + endgenerate + assign cout = c << 1; +endmodule diff --git a/source_code/rv32m/flex_counter_mul.sv b/source_code/rv32m/flex_counter_mul.sv new file mode 100644 index 000000000..9bce1660c --- /dev/null +++ b/source_code/rv32m/flex_counter_mul.sv @@ -0,0 +1,38 @@ +module flex_counter_mul #( + parameter int NUM_CNT_BITS = 4 +) ( + input wire clk, + input wire n_rst, + input wire clear, + input wire count_enable, + input reg [(NUM_CNT_BITS-1):0] rollover_val, + output reg [(NUM_CNT_BITS-1):0] count_out, + output reg rollover_flag +); + + reg [(NUM_CNT_BITS-1):0] next_count; + reg next_flag; + always_ff @(posedge clk, negedge n_rst) begin + if (n_rst == 0) begin + count_out <= '0; + rollover_flag <= 1'b0; + end else begin + count_out <= next_count; + rollover_flag <= next_flag; + end + end + + always_comb begin + if (clear == 1) next_flag = 0; + else if (count_enable == 0) next_flag = rollover_flag; + else if (count_out == (rollover_val - 1) & clear == 1'b0) next_flag = 1; + else next_flag = 0; + + + if (clear) next_count = 0; + else if (count_enable) begin + if (count_out == rollover_val) next_count = 1; + else next_count = count_out + 1; + end else next_count = count_out; + end +endmodule diff --git a/source_code/rv32m/full_adder.sv b/source_code/rv32m/full_adder.sv new file mode 100644 index 000000000..d531e031c --- /dev/null +++ b/source_code/rv32m/full_adder.sv @@ -0,0 +1,11 @@ +module full_adder ( + input logic x, + input logic y, + input logic cin, + output logic cout, + output logic sum +); + assign sum = x ^ y ^ cin; + assign cout = (x & y) | (x & cin) | (y & cin); + +endmodule diff --git a/source_code/rv32m/pp_mul32.sv b/source_code/rv32m/pp_mul32.sv new file mode 100644 index 000000000..0c4a0b4f4 --- /dev/null +++ b/source_code/rv32m/pp_mul32.sv @@ -0,0 +1,347 @@ +// Pipelined multiplier - 32 bits +module pp_mul32 ( + input logic CLK, + input logic nRST, + input logic [31:0] multiplicand, + input logic [31:0] multiplier, + input logic [1:0] is_signed, + input logic start, + output logic finished, + output logic [63:0] product +); + //logic start_reg; + logic [31:0] multiplicand_reg; + logic [31:0] multiplier_reg; + logic [63:0] result; + logic [63:0] result2; + logic [63:0] temp_product; + logic [63:0] temp_product2; + logic [31:0] multiplicand_mod; + logic [31:0] multiplier_mod; + logic adjust_product; + logic [63:0] partial_product[16]; + logic [63:0] + pp0, pp1, pp2, pp3, pp4, pp5, pp6, pp7, pp8, pp9, pp10, pp11, pp12, pp13, pp14, pp15; + logic [32:0] mul_plus2, mul_minus2, mul_minus1; + logic [63:0] pp[16]; + logic [32:0] modified_in; + logic [63:0] + sum0, sum1, sum2, sum3, sum4, sum5, sum6, sum7, sum8, sum9, sum10, sum11, sum12, sum13; + logic [63:0] + cout0, + cout1, + cout2, + cout3, + cout4, + cout5, + cout6, + cout7, + cout8, + cout9, + cout10, + cout11, + cout12, + cout13; + logic [1:0] count; + logic mult_complete; + //logic [63:0] sum13_pip, cout13_pip; + logic [63:0] sum5_pip, cout5_pip, sum6_pip, cout6_pip, sum7_pip, cout7_pip; + logic [1:0] is_signed_reg; + logic done; + logic count_ena; + integer i, j; + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + multiplicand_reg <= '0; + multiplier_reg <= '0; + is_signed_reg <= '0; + end else if (start) begin + multiplicand_reg <= multiplicand; + multiplier_reg <= multiplier; + is_signed_reg <= is_signed; + end + end + // Modify multiplicand and multiplier if they are signed + assign multiplicand_mod = is_signed_reg[1] && multiplicand_reg[31] ? + (~(multiplicand_reg)+1) : multiplicand_reg; + assign multiplier_mod = is_signed_reg[0] && multiplier_reg[31] ? + (~(multiplier_reg)+1) : multiplier_reg; + // Control signal to modify final product + assign adjust_product = (is_signed_reg[0] & multiplier_reg[31]) + ^ (is_signed_reg[1] & multiplicand_reg[31]); + // For bit pair recoding part + assign mul_plus2 = multiplicand_mod + multiplicand_mod; + assign mul_minus2 = ~mul_plus2 + 1; + assign mul_minus1 = ~multiplicand_mod + 1; + assign modified_in = {multiplier_mod, 1'b0}; + + // STAGE 1: BOOTH ENCODER + // Bit pair recoding to generate partial product + always_comb begin + for (i = 0; i < 32; i = i + 2) begin + case ({ + modified_in[i+2], modified_in[i+1], modified_in[i] + }) + 3'b000: pp[i/2] = '0; //0 + 3'b001: pp[i/2] = {{32'd0}, multiplicand_mod}; // +1M + 3'b010: pp[i/2] = {{32'd0}, multiplicand_mod}; // +1M + 3'b011: pp[i/2] = {{31'd0}, mul_plus2}; // +2M + 3'b100: + if (mul_minus2 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus2}; // -2M + 3'b101: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus1}; // -1M + 3'b110: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus1}; // -1M + 3'b111: pp[i/2] = '0; + endcase + end + end + // Shift partial product + always_comb begin + for (j = 0; j < 16; j = j + 1) begin + partial_product[j] = pp[j] << (2 * j); // Shift with multiple of 2 (Radix 4) + end + end + + // Pipeline register before wallace tree + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + pp0 <= '0; + pp1 <= '0; + pp2 <= '0; + pp3 <= '0; + pp4 <= '0; + pp5 <= '0; + pp6 <= '0; + pp7 <= '0; + pp8 <= '0; + pp9 <= '0; + pp10 <= '0; + pp11 <= '0; + pp12 <= '0; + pp13 <= '0; + pp14 <= '0; + pp15 <= '0; + end else begin + pp0 <= partial_product[0]; + pp1 <= partial_product[1]; + pp2 <= partial_product[2]; + pp3 <= partial_product[3]; + pp4 <= partial_product[4]; + pp5 <= partial_product[5]; + pp6 <= partial_product[6]; + pp7 <= partial_product[7]; + pp8 <= partial_product[8]; + pp9 <= partial_product[9]; + pp10 <= partial_product[10]; + pp11 <= partial_product[11]; + pp12 <= partial_product[12]; + pp13 <= partial_product[13]; + pp14 <= partial_product[14]; + pp15 <= partial_product[15]; + end + end + + // STAGE 2: WALLACE TREE + // Layer 1 + carry_save_adder #(64) CSA0 ( + .x(pp0), + .y(pp1), + .z(pp2), + .cout(cout0), + .sum(sum0) + ); + carry_save_adder #(64) CSA1 ( + .x(pp3), + .y(pp4), + .z(pp5), + .cout(cout1), + .sum(sum1) + ); + carry_save_adder #(64) CSA2 ( + .x(pp6), + .y(pp7), + .z(pp8), + .cout(cout2), + .sum(sum2) + ); + carry_save_adder #(64) CSA3 ( + .x(pp9), + .y(pp10), + .z(pp11), + .cout(cout3), + .sum(sum3) + ); + carry_save_adder #(64) CSA4 ( + .x(pp12), + .y(pp13), + .z(pp14), + .cout(cout4), + .sum(sum4) + ); // remaining partialproduct 15 + // Layer 2 + carry_save_adder #(64) CSA5 ( + .x(cout0), + .y(sum0), + .z(cout1), + .cout(cout5), + .sum(sum5) + ); + carry_save_adder #(64) CSA6 ( + .x(sum1), + .y(cout2), + .z(sum2), + .cout(cout6), + .sum(sum6) + ); + carry_save_adder #(64) CSA7 ( + .x(cout3), + .y(sum3), + .z(cout4), + .cout(cout7), + .sum(sum7) + ); // remaining sum4 + // Pipeline register in wallace tree between layer 2 and layer 3 + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + cout5_pip <= '0; + sum5_pip <= '0; + cout6_pip <= '0; + sum6_pip <= '0; + cout7_pip <= '0; + sum7_pip <= '0; + end else begin + cout5_pip <= cout5; + sum5_pip <= sum5; + cout6_pip <= cout6; + sum6_pip <= sum6; + cout7_pip <= cout7; + sum7_pip <= sum7; + end + end + + // Layer 3 + carry_save_adder #(64) CSA8 ( + .x(cout5), + .y(sum5), + .z(cout6), + .cout(cout8), + .sum(sum8) + ); + carry_save_adder #(64) CSA9 ( + .x(sum6), + .y(cout7), + .z(sum7), + .cout(cout9), + .sum(sum9) + ); + // Layer 4 + carry_save_adder #(64) CSA10 ( + .x(cout8), + .y(sum8), + .z(cout9), + .cout(cout10), + .sum(sum10) + ); + carry_save_adder #(64) CSA11 ( + .x(sum9), + .y(pp15), + .z(sum4), + .cout(cout11), + .sum(sum11) + ); + // Layer 5 + carry_save_adder #(64) CSA12 ( + .x(cout10), + .y(sum10), + .z(cout11), + .cout(cout12), + .sum(sum12) + ); // remaining sum11 + // Layer 6 + carry_save_adder #(64) CSA13 ( + .x(cout12), + .y(sum12), + .z(sum11), + .cout(cout13), + .sum(sum13) + ); + + // STAGE 3: NORMAL ADDER + flex_counter_mul #(2) FC ( + .clk(CLK), + .n_rst(nRST), + .clear(start), + .count_enable(count_ena), + .rollover_val(2'd2), + .count_out(count), + .rollover_flag(finished) + ); + assign temp_product = cout13 + sum13; + assign temp_product2 = is_signed_reg[0] == 0 && multiplier_reg[31] ? + temp_product + ({{33{multiplicand_mod[31]}},multiplicand_mod} << 32) + : temp_product; // plus extra 1M + assign result = adjust_product ? (~temp_product2) + 1 : temp_product2; + assign mult_complete = count == 2'd1 | count == 2'd2; + assign result2 = mult_complete ? result : '0; + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + product <= '0; + end else begin + product <= result2; + end + end + + //Small FSM to control flex counter + typedef enum logic { + IDLE, + START + } state_t; + state_t state, next_state; + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) state <= IDLE; + else state <= next_state; + end + + always_comb begin + /* + next_state = state; + case (state) + IDLE: begin + if (start) + next_state = START; + end + START: begin + if (finished) + next_state = IDLE; + end + endcase + */ + next_state = state; + if (state == IDLE && start) begin + next_state = START; + end else if (state == START && finished) begin + next_state = IDLE; + end else begin + next_state = state; + end + end + + always_comb begin + count_ena = 0; + case (state) + IDLE: begin + count_ena = 0; + end + START: begin + count_ena = ~finished; + end + endcase + end + +endmodule diff --git a/source_code/rv32m/radix4_divider.sv b/source_code/rv32m/radix4_divider.sv new file mode 100644 index 000000000..361b60b6c --- /dev/null +++ b/source_code/rv32m/radix4_divider.sv @@ -0,0 +1,141 @@ +module radix4_divider #( + parameter int NUM_BITS = 32 +) ( + input logic CLK, + input logic nRST, + input logic start, + input logic is_signed, //new + input logic [NUM_BITS-1:0] dividend, + input logic [NUM_BITS-1:0] divisor, + output logic [NUM_BITS-1:0] quotient, + output logic [NUM_BITS-1:0] remainder, + output logic finished + +); + logic [NUM_BITS-1:0] + next_remainder, + next_quotient, + shifted_remainder, + shifted_quotient, + temp_quotient, + temp_remainder; + logic [NUM_BITS:0] Result1, Result2, Result3; + logic [NUM_BITS-1:0] DivisorX2, DivisorX3; + logic [4:0] count, next_count; + + logic [NUM_BITS-1:0] usign_divisor, usign_dividend; + logic adjustment_possible, adjust_quotient, adjust_remainder; + logic div_done; + + assign usign_divisor = is_signed & divisor[NUM_BITS-1] ? (~divisor) + 1 : divisor; + assign usign_dividend = is_signed & dividend[NUM_BITS-1] ? (~dividend) + 1 : dividend; + assign adjustment_possible = is_signed && (divisor[NUM_BITS-1] ^ dividend[NUM_BITS-1]); + assign adjust_quotient = adjustment_possible && ~quotient[NUM_BITS-1]; + assign adjust_remainder = is_signed && dividend[NUM_BITS-1]; + assign div_done = (count == 0); + assign quotient = temp_quotient; + assign remainder = temp_remainder; + + /* + always_comb begin + quotient = temp_quotient; + remainder = temp_remainder; + if (count == 5'b1) begin + quotient = adjust_quotient ? ~temp_quotient + 1 : temp_quotient; + remainder = adjust_remainder ? ~temp_remainder + 1 : temp_remainder; + end + end +*/ + /* + always_ff @(posedge CLK, negedge nRST) begin + if (~finished && adjust_quotient) + quotient <= ~quotient + 1; + + else if(~finished && adjust_remainder ) + remainder <= ~remainder + 1; + + else begin + quotient <= quotient; + remainder <= remainder; + end + end +*/ + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + finished <= 1'b0; + end else if (start) begin + finished <= 1'b0; + end else if (div_done) begin + finished <= 1'b1; + end + end + //initialize d2 d3 + assign DivisorX2 = usign_divisor << 1; //Divisor*2 + assign DivisorX3 = (usign_divisor << 1) + usign_divisor; //Divisor*3 + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + + count <= 5'd16; + temp_quotient <= '0; + temp_remainder <= '0; + end else if (start) begin + temp_quotient <= usign_dividend; + temp_remainder <= '0; + count <= 5'd16; + + end else begin + temp_quotient <= next_quotient; + temp_remainder <= next_remainder; + count <= next_count; + end + end + + always_comb begin + + next_quotient = temp_quotient; + next_remainder = temp_remainder; + next_count = count; + shifted_remainder = '0; + shifted_quotient = '0; + Result1 = '0; + Result2 = '0; + Result3 = '0; + + if (count != 0) begin + next_count = count - 1; + shifted_remainder = (temp_remainder << 2) | temp_quotient[NUM_BITS-1:NUM_BITS-2]; + shifted_quotient = temp_quotient << 2; + Result1 = shifted_remainder - usign_divisor; + Result2 = shifted_remainder - DivisorX2; + Result3 = shifted_remainder - DivisorX3; + if (Result1[NUM_BITS-1] | Result1[NUM_BITS]) begin + next_remainder = shifted_remainder; + next_quotient = shifted_quotient | 0; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + + end else if (Result2[NUM_BITS-1] | Result2[NUM_BITS]) begin + next_remainder = Result1[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 1; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end else if (Result3[NUM_BITS-1] | Result3[NUM_BITS]) begin + next_remainder = Result2[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 2; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end else begin + next_remainder = Result3[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 3; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end + end + + end +endmodule diff --git a/source_code/rv32m/rv32m.core b/source_code/rv32m/rv32m.core new file mode 100644 index 000000000..3a634dce8 --- /dev/null +++ b/source_code/rv32m/rv32m.core @@ -0,0 +1,27 @@ +CAPI=2: +name: socet:riscv:rv32m:0.1.0 +description: RV32M Extension for RISCVBusiness + +filesets: + rtl: + depend: + - "socet:riscv:riscv_include" + - "socet:riscv:packages" + files: + #- rv32m_pkg.sv + - carry_save_adder.sv + - full_adder.sv + - flex_counter_mul.sv + - pp_mul32.sv + - radix4_divider.sv + - rv32m_decode.sv + - rv32m_disabled.sv + - rv32m_enabled.sv + - rv32m_wrapper.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + toplevel: rv32m_wrapper \ No newline at end of file diff --git a/source_code/rv32m/rv32m_decode.sv b/source_code/rv32m/rv32m_decode.sv new file mode 100644 index 000000000..802d1995a --- /dev/null +++ b/source_code/rv32m/rv32m_decode.sv @@ -0,0 +1,40 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32m_decode.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/07/2017 +* Description: Decoding for standard multiply extension +*/ +module rv32m_decode ( + input [31:0] insn, + output logic claim, + output rv32m_pkg::rv32m_decode_t rv32m_control +); + + import rv32m_pkg::*; + + rv32m_insn_t insn_split = rv32m_insn_t'(insn); + + assign claim = (insn_split.opcode_major == RV32M_OPCODE) + && (insn_split.opcode_minor == RV32M_OPCODE_MINOR); + + assign rv32m_control.select = claim; + assign rv32m_control.op = rv32m_op_t'(insn_split.funct); + +endmodule diff --git a/source_code/rv32m/rv32m_disabled.sv b/source_code/rv32m/rv32m_disabled.sv new file mode 100644 index 000000000..21fec2181 --- /dev/null +++ b/source_code/rv32m/rv32m_disabled.sv @@ -0,0 +1,16 @@ + +module rv32m_disabled( + input CLK, + input nRST, + input rv32m_start, + input rv32m_pkg::rv32m_op_t operation, + input [31:0] rv32m_a, + input [31:0] rv32m_b, + output rv32m_busy, + output logic [31:0] rv32m_out +); + + assign rv32m_busy = 1'b0; + assign rv32m_out = 32'b0; + +endmodule \ No newline at end of file diff --git a/source_code/rv32m/rv32m_enabled.sv b/source_code/rv32m/rv32m_enabled.sv new file mode 100644 index 000000000..6d1004f53 --- /dev/null +++ b/source_code/rv32m/rv32m_enabled.sv @@ -0,0 +1,227 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32m_execute.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/07/2017 +* Description: Execute stage for standard RV32M +*/ +`include "component_selection_defines.vh" + +module rv32m_enabled ( + input CLK, + input nRST, + input rv32m_start, + input rv32m_pkg::rv32m_op_t operation, + input [31:0] rv32m_a, + input [31:0] rv32m_b, + output rv32m_busy, + output [31:0] rv32m_out +); + + import rv32m_pkg::*; + import rv32i_types_pkg::*; + + + /* Operand Saver to detect new request */ + + // operand saver + word_t op_a, op_b, op_a_save, op_b_save; + rv32m_op_t operation_save; + //logic [2:0] operation, operation_save; + //logic [1:0] is_signed_save, is_signed_curr, is_signed; + logic operand_diff; + logic is_multiply; + logic is_divide; + logic [1:0] is_signed; + + assign is_multiply = (operation == MUL) || (operation == MULH) || (operation == MULHU) || (operation == MULHSU); + assign is_divide = (operation == DIV) || (operation == DIVU) || (operation == REM) || (operation == REMU); + + + assign op_a = operand_diff ? rv32m_a : op_a_save; + assign op_b = operand_diff ? rv32m_b : op_b_save; + assign operand_diff = rv32m_start && ((op_a_save != rv32m_a) || (op_b_save != rv32m_b) || (operation_save != operation)); + /*assign operand_diff = ((op_a_save != rv32m_a) || + (op_b_save != rv32m_b) || + (is_signed_save != is_signed_curr) || + (operation_save != {idex.mul, idex.div, idex.rem})) && + idex.start ; + assign is_signed_curr = idex.usign_usign ? 2'b00 : (idex.sign_sign ? 2'b11 : 2'b10); + // Is signed + operation = func3? Seems like we could potentially just save off the func3 wholesale + assign is_signed = operand_diff ? is_signed_curr : is_signed_save; + assign operation = operand_diff ? {idex.mul, idex.div, idex.rem} : operation_save;*/ + + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) begin + op_a_save <= '0; + op_b_save <= '0; + //is_signed_save <= '0; + operation_save <= '0; + end else if (operand_diff) begin + op_a_save <= rv32m_a; + op_b_save <= rv32m_b; + //is_signed_save <= is_signed_curr; + operation_save <= operation; + end + end + + + /* MULTIPLICATION */ + + // multiplier signals + word_t multiplicand, multiplier; + logic [(WORD_SIZE*2)-1:0] product; + logic mul_finished; + logic mul_start; + + assign multiplicand = op_a; + assign multiplier = op_b; + assign mul_start = operand_diff && is_multiply && rv32m_start; + + // Module instantiations + // TODO: Case for which multiplier/divider to use + pp_mul32 mult_i ( + .CLK(CLK), + .nRST(nRST), + .multiplicand(multiplicand), + .multiplier(multiplier), + .product(product), + .is_signed(is_signed), + .start(mul_start), + .finished(mul_finished) + ); + + + /* DIVISION / REMAINDER */ + + logic overflow, div_zero, div_finished; + word_t divisor, dividend, quotient, remainder, divisor_save, dividend_save; + logic div_operand_diff; + logic div_start; + + assign divisor = op_b; + assign dividend = op_a; + assign overflow = (dividend == 32'h8000_0000) && (divisor == 32'hffff_ffff) && is_signed[0]; + assign div_zero = (divisor == 32'h0); + assign div_start = operand_diff && is_divide && !overflow && !div_zero && rv32m_start; + + radix4_divider div_i ( + .CLK(CLK), + .nRST(nRST), + .divisor(divisor), + .dividend(dividend), + .is_signed(is_signed[0]), // For division, only 00 or 11, input is 1 bit, so take one of the bits for "is_signed" (arbitrary) + .start(div_start), + .remainder(remainder), + .quotient(quotient), + .finished(div_finished) + ); + + /* Operation decoding */ + always_comb begin + casez (operation) + MUL, MULH, DIV, REM: is_signed = 2'b11; + MULHU, DIVU, REMU: is_signed = 2'b00; + MULHSU: is_signed = 2'b10; + default: is_signed = 2'b11; + endcase + end + + /* Result */ + always_comb begin + if(rv32m_start) begin + // Note: operand_diff on all these cases is to fix condition where + // "done" flag asserted by FU due to previous op. RV32M will always + // take at least 1 extra cycle if we aren't reusing a value. + casez(operation) + MUL: begin + rv32m_busy = operand_diff || !mul_finished; + rv32m_out = product[WORD_SIZE-1:0]; + end + + MULH, MULHU, MULHSU: begin + rv32m_busy = operand_diff || !mul_finished; + rv32m_out = product[(WORD_SIZE*2)-1 : WORD_SIZE]; + end + + // TODO: Is there a better way to decode this? Lots of repetition. + DIV: begin + rv32m_busy = operand_diff || (!div_finished && !div_zero && !overflow); + rv32m_out = div_zero ? 32'hffff_ffff : (overflow ? 32'h8000_0000 : quotient); + end + + DIVU: begin + rv32m_busy = operand_diff || (!div_finished && !div_zero && !overflow); + rv32m_out = div_zero ? 32'h7fff_ffff : (overflow ? 32'h8000_0000 : quotient); + end + + REM, REMU: begin + rv32m_busy = operand_diff || (!div_finished && !div_zero && !overflow); + rv32m_out = div_zero ? dividend : (overflow ? 32'h0000_0000 : remainder); + end + + default: begin + rv32m_busy = 1'b0; + rv32m_out = 32'b0; // TODO: Should this return BAD3? + end + endcase + end else begin + rv32m_busy = 1'b0; + rv32m_out = 32'b0; + end + end + + /* + always_comb begin + casez (operation) + 3'b1??: begin // MUL + eif.busy = ~mul_finished; + eif.reg_wdata = idex.lower_word ? + product[WORD_SIZE-1:0] + : product[(WORD_SIZE*2)-1 : WORD_SIZE]; + end + 3'b01?: begin // DIV + eif.busy = ~div_finished & ~(div_zero | overflow); + if (div_zero) begin + eif.reg_wdata = idex.sign_sign ? 32'hffff_ffff : 32'h7fff_ffff; + end else if (overflow) begin + eif.reg_wdata = 32'h8000_0000; + end else begin + eif.reg_wdata = quotient; + end + end + 3'b001: begin // REM + eif.busy = ~div_finished & ~(div_zero | overflow); + if (div_zero) begin + eif.reg_wdata = dividend; + end else if (overflow) begin + eif.reg_wdata = 32'h0000_0000; + end else begin + eif.reg_wdata = remainder; + end + end + default: begin + eif.busy = 1'b0; + eif.reg_wdata = 32'hBAD3_BAD3; + end + endcase + end + */ + +endmodule diff --git a/source_code/rv32m/rv32m_wrapper.sv b/source_code/rv32m/rv32m_wrapper.sv new file mode 100644 index 000000000..1641da5a7 --- /dev/null +++ b/source_code/rv32m/rv32m_wrapper.sv @@ -0,0 +1,27 @@ +`include "component_selection_defines.vh" + +module rv32m_wrapper( + input CLK, + input nRST, + input rv32m_start, + input rv32m_pkg::rv32m_op_t operation, + input [31:0] rv32m_a, + input [31:0] rv32m_b, + output rv32m_busy, + output logic [31:0] rv32m_out +); + import rv32m_pkg::*; + + /*generate + case(RV32M_ENABLED) + "disabled": rv32m_disabled RV32M(.*); + "enabled": rv32m_enabled RV32M(.*); + endcase + endgenerate*/ + `ifdef RV32M_SUPPORTED + rv32m_enabled RV32M(.*); + `else + rv32m_disabled RV32M(.*); + `endif + +endmodule \ No newline at end of file diff --git a/source_code/rv32m/shift_add_multiplier.sv b/source_code/rv32m/shift_add_multiplier.sv new file mode 100644 index 000000000..41227fb89 --- /dev/null +++ b/source_code/rv32m/shift_add_multiplier.sv @@ -0,0 +1,78 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: shift_add_multiplier.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/15/2017 +* Description: N bit parameterized shift and add multiplier +* Takes up to N+1 cycles to compute the product. +*/ + +module shift_add_multiplier #( + parameter int N = 32 +) ( + input logic CLK, + nRST, + input logic [N-1:0] multiplicand, + input logic [N-1:0] multiplier, + input logic [1:0] is_signed, + input logic start, + output logic [(N*2)-1:0] product, + output logic finished +); + + logic [(N*2)-1:0] multiplier_reg, multiplicand_reg; + logic [(N*2)-1:0] multiplier_ext, multiplicand_ext; + logic [(N*2)-1:0] partial_product; + logic mult_complete, adjust_product; + + assign mult_complete = !(|multiplier_reg); + assign adjust_product = (is_signed[0] & multiplier[N-1]) ^ (is_signed[1] & multiplicand[N-1]); + assign partial_product = multiplier_reg[0] ? multiplicand_reg : '0; + assign multiplier_ext = (~{{N{multiplier[N-1]}},multiplier}) + 1; + assign multiplicand_ext = (~{{N{multiplicand[N-1]}},multiplicand}) + 1; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) finished <= 1'b0; + else if (start) finished <= 1'b0; + else if (mult_complete) finished <= 1'b1; + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + multiplicand_reg <= '0; + multiplier_reg <= '0; + product <= '0; + end else if (start) begin + multiplicand_reg <= (is_signed[1] && multiplicand[N-1]) ? + multiplicand_ext : {{N{1'b0}}, multiplicand}; + multiplier_reg <= (is_signed[0] && multiplier[N-1]) ? + multiplier_ext : {{N{1'b0}}, multiplier}; + product <= '0; + end else if (mult_complete & ~finished) begin // adjust sign on product + multiplicand_reg <= multiplicand_reg; + multiplier_reg <= multiplier_reg; + product <= adjust_product ? (~product) + 1 : product; + end else if (~finished) begin + multiplicand_reg <= multiplicand_reg << 1; + multiplier_reg <= multiplier_reg >> 1; + product <= product + partial_product; + end + end + +endmodule diff --git a/source_code/rv32m/shift_test_restore_divider.sv b/source_code/rv32m/shift_test_restore_divider.sv new file mode 100644 index 000000000..ba9e441cd --- /dev/null +++ b/source_code/rv32m/shift_test_restore_divider.sv @@ -0,0 +1,92 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: shift_test_restore_divider.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/21/2017 +* Description: NxN bit divider using the shift-test-restore algorithm +*/ + +module shift_test_restore_divider #( + parameter int N = 32 +) ( + input logic CLK, + nRST, + input logic [N-1:0] divisor, + dividend, + input logic is_signed, + input logic start, + output logic [N-1:0] remainder, + quotient, + output logic finished +); + + localparam int COUNTER_BITS = $clog2(N) + 1; + localparam int U_Q = N - 1; + localparam int U_R = (2 * N) - 1; + + logic [(2*N)+1:0] result; + assign {remainder, quotient} = result[(2*N)-1:0]; + logic test_phase; + logic [COUNTER_BITS-1:0] counter; + logic [N-1:0] usign_divisor, usign_dividend; + logic adjustment_possible, adjust_quotient, adjust_remainder; + logic div_done; + + assign usign_divisor = is_signed & divisor[N-1] ? (~divisor) + 1 : divisor; + assign usign_dividend = is_signed & dividend[N-1] ? (~dividend) + 1 : dividend; + assign adjustment_possible = is_signed && (divisor[N-1] ^ dividend[N-1]); + assign adjust_quotient = adjustment_possible && ~quotient[N-1]; + assign adjust_remainder = is_signed && dividend[N-1]; + assign div_done = (counter == 0); + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + result <= '0; + counter <= N; + test_phase <= 1'b0; + end else if (start) begin + result <= {{(N - 1) {1'b0}}, usign_dividend, 1'b0}; + counter <= N; + test_phase <= 1'b0; + end else if (counter > 0) begin + if (~test_phase) begin // shift and sub + result[U_R+1-:N+1] <= result[U_R+1-:N+1] - usign_divisor; + end else begin // check result + counter <= counter - 1; + if (result[U_R+1]) // negative remainder, must restore + result <= {(result[U_R+1-:N+1] + usign_divisor), result[U_Q:0]} << 1; + else result <= {result[U_R-1:0], 1'b1}; + end + test_phase <= ~test_phase; + end else if (~finished) begin + if (adjust_quotient) result[U_Q:0] <= (~result[U_Q:0]) + 1; + if (adjust_remainder) result[U_R-:N] <= (~result[U_R+1-:N]) + 1; + //result[U_R-:N] <= (~({result[U_R],result[U_R-:N-1]}))+1; + else + result[U_R-:N] <= result[U_R+1-:N]; + end + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) finished <= 1'b0; + else if (start) finished <= 1'b0; + else if (div_done) finished <= 1'b1; + end + +endmodule diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index 161e703a7..6c8eb7104 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -143,12 +143,13 @@ module RISCVBusiness ( .mtime(mtime) ); +/* TODO: Adding back RISC-MGMT to 3-stage pipeline risc_mgmt_wrapper rmgmt ( .CLK (CLK), .nRST (nRST), .rm_if(rm_if) ); - +*/ /* caches_wrapper caches ( .CLK(CLK), diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index 72814024a..364e6b01e 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -27,6 +27,7 @@ `include "rv32i_reg_file_if.vh" `include "risc_mgmt_if.vh" `include "decompressor_if.vh" +`include "component_selection_defines.vh" module control_unit ( control_unit_if.control_unit cu_if, @@ -40,6 +41,7 @@ module control_unit ( import alu_types_pkg::*; import rv32i_types_pkg::*; import machine_mode_types_1_12_pkg::*; + import rv32m_pkg::*; stype_t instr_s; itype_t instr_i; @@ -48,6 +50,12 @@ module control_unit ( utype_t instr_u; ujtype_t instr_uj; + // Set if base ISA doesn't have this instruction, but overriden by claim from extension + logic maybe_illegal; + logic claimed; + // Per-extension claim signals + logic rv32m_claim; + assign instr_s = stype_t'(cu_if.instr); assign instr_i = itype_t'(cu_if.instr); assign instr_r = rtype_t'(cu_if.instr); @@ -115,7 +123,7 @@ module control_unit ( LOAD: cu_if.w_sel = 3'd0; JAL, JALR: cu_if.w_sel = 3'd1; LUI: cu_if.w_sel = 3'd2; - IMMED, AUIPC, REGREG: cu_if.w_sel = 3'd3; + IMMED, AUIPC, REGREG: cu_if.w_sel = 3'd3; // RV32M: Opcodes are REGREG, no change needed SYSTEM: cu_if.w_sel = 3'd4; default: cu_if.w_sel = 3'd0; endcase @@ -190,13 +198,16 @@ module control_unit ( always_comb begin case (cu_if.opcode) - REGREG: cu_if.illegal_insn = instr_r.funct7[0]; + REGREG: maybe_illegal = instr_r.funct7[0]; LUI, AUIPC, JAL, JALR, BRANCH, LOAD, STORE, IMMED, SYSTEM, MISCMEM, opcode_t'('0): - cu_if.illegal_insn = 1'b0; - default: cu_if.illegal_insn = 1'b1; + maybe_illegal = 1'b0; + default: maybe_illegal = 1'b1; endcase end + assign cu_if.illegal_insn = maybe_illegal && !claimed; + assign claimed = rv32m_claim; // Add OR conditions for new extensions + //Decoding of System Priv Instructions always_comb begin cu_if.ret_insn = 1'b0; @@ -245,4 +256,16 @@ module control_unit ( assign cu_if.csr_addr = csr_addr_t'(instr_i.imm11_00); assign cu_if.zimm = cu_if.instr[19:15]; + // Extension decoding + `ifdef RV32M_SUPPORTED + rv32m_decode RV32M_DECODE( + .insn(cu_if.instr), + .claim(rv32m_claim), + .rv32m_control(cu_if.rv32m_control) + ); + `else + assign cu_if.rv32m_control = {1'b0, rv32m_op_t'(0)}; + assign rv32m_claim = 1'b0; + `endif // RV32M_SUPPORTED + endmodule diff --git a/source_code/standard_core/top_core.sv b/source_code/standard_core/top_core.sv index 2a862ee9f..88b7b5f50 100644 --- a/source_code/standard_core/top_core.sv +++ b/source_code/standard_core/top_core.sv @@ -40,6 +40,7 @@ module top_core #( get_x28 = CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[28]; endfunction + bind stage3_mem_stage cpu_tracker cpu_track1 ( .CLK(CLK), .wb_stall(wb_stall), From cc03b4fa7fbaf5658953192bac0a541ae66a79de Mon Sep 17 00:00:00 2001 From: Hadi Ahmed Date: Sun, 11 Dec 2022 16:00:24 -0500 Subject: [PATCH 30/47] Added new privileged unit changes to core --- source_code/include/prv_pipeline_if.vh | 11 +++++----- .../stage3/include/stage3_hazard_unit_if.vh | 22 +++++++++---------- .../stage3/source/stage3_execute_stage.sv | 11 +++++----- .../stage3/source/stage3_hazard_unit.sv | 7 +++--- .../stage3/source/stage3_mem_stage.sv | 16 ++++++++------ .../stage3/source/stage3_types_pkg.sv | 2 ++ 6 files changed, 37 insertions(+), 32 deletions(-) diff --git a/source_code/include/prv_pipeline_if.vh b/source_code/include/prv_pipeline_if.vh index dee0456c0..9b6803a66 100644 --- a/source_code/include/prv_pipeline_if.vh +++ b/source_code/include/prv_pipeline_if.vh @@ -64,20 +64,19 @@ interface prv_pipeline_if(); logic prot_fault_s, prot_fault_l, prot_fault_i; modport hazard ( - input priv_pc, insert_pc, intr, + input priv_pc, insert_pc, intr, prot_fault_s, prot_fault_l, prot_fault_i, output pipe_clear, ret, epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env, badaddr, wb_enable, + breakpoint, env, wfi, badaddr, wb_enable, ex_rmgmt, ex_rmgmt_cause ); modport pipe ( - output swap, clr, set, read_only, wdata, csr_addr, valid_write, instr, dren, dwen, daddr, d_acc_width, wfi, - input rdata, invalid_priv_isn, prot_fault_s, prot_fault_l + output swap, clr, set, read_only, wdata, csr_addr, valid_write, instr, dren, dwen, daddr, d_acc_width, + input rdata, invalid_priv_isn ); modport fetch ( - input prot_fault_i, output iren, iaddr, i_acc_width ); @@ -89,7 +88,7 @@ interface prv_pipeline_if(); ex_rmgmt, ex_rmgmt_cause, daddr, iaddr, dren, dwen, iren, d_acc_width, i_acc_width, - output priv_pc, insert_pc, intr, rdata, invalid_priv_isn, + output priv_pc, insert_pc, intr, rdata, invalid_priv_isn, prot_fault_s, prot_fault_l, prot_fault_i ); diff --git a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh index ab4b34648..58b03f852 100644 --- a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh +++ b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 06/15/2016 -* Description: Interface for the hazard unit of the two stage pipeline +* Description: Interface for the hazard unit of the two stage pipeline */ `ifndef STAGE3_HAZARD_UNIT_IF_VH @@ -53,10 +53,10 @@ interface stage3_hazard_unit_if(); //Pipeline Exceptions (inputs) logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m; + breakpoint, env, wfi; word_t badaddr; - // Pipeline Tokens + // Pipeline Tokens logic token_ex; logic token_mem; @@ -68,11 +68,11 @@ interface stage3_hazard_unit_if(); reg_write, csr_read, i_mem_busy, d_mem_busy, dren, dwen, ret, jump, branch, fence_stall, mispredict, halt, pc_f, pc_e, pc_m, - fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, + fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env, wfi, badaddr, ifence, token_ex, token_mem, rv32c_ready, valid_e, valid_m, ex_busy, - output pc_en, npc_sel, + output pc_en, npc_sel, if_ex_flush, ex_mem_flush, if_ex_stall, ex_mem_stall, priv_pc, insert_priv_pc, iren, suppress_iren, suppress_data, rollback @@ -91,13 +91,13 @@ interface stage3_hazard_unit_if(); modport mem ( input ex_mem_stall, ex_mem_flush, suppress_data, output rd_m, reg_write, csr_read, - d_mem_busy, dren, dwen, ret, + d_mem_busy, dren, dwen, ret, jump, branch, fence_stall, mispredict, halt, pc_m, valid_m, - fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, + fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env, badaddr, ifence, token_mem ); - + endinterface `endif diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index 437f4cb79..1bd3967de 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -40,7 +40,6 @@ module stage3_execute_stage ( stage3_mem_pipe_if.execute ex_mem_if, stage3_hazard_unit_if.execute hazard_if, stage3_forwarding_unit_if.execute fw_if, - //prv_pipeline_if.pipe prv_pipe_if, //risc_mgmt_if.ts_execute rm_if, sparce_pipeline_if.pipe_execute sparce_if, rv32c_if.execute rv32cif @@ -113,7 +112,7 @@ module stage3_execute_stage ( alu alu (.*); jump_calc jump_calc (.*); branch_res branch_res (.br_if(branch_if)); - + rv32m_wrapper RV32M_FU ( .CLK, .nRST, @@ -130,7 +129,7 @@ module stage3_execute_stage ( word_t rs1_post_fwd, rs2_post_fwd; assign rs1_post_fwd = fw_if.fwd_rs1 ? fw_if.rd_mem_data : rf_if.rs1_data; assign rs2_post_fwd = fw_if.fwd_rs2 ? fw_if.rd_mem_data : rf_if.rs2_data; - + /****************** * Sign Extensions @@ -194,7 +193,7 @@ module stage3_execute_stage ( assign rf_if.w_data = ex_mem_if.reg_wdata; assign rf_if.rd = ex_mem_if.rd_m; assign rf_if.wen = ex_mem_if.reg_write && !hazard_if.ex_mem_stall; // TODO: The second signal only matters for some miniscule power reduction by not writing each cycle. This is correct with only the wen signal due to no loop from reg read to reg write - + /*********************************************** * Branch Target Resolution and Associated Logic ***********************************************/ @@ -237,7 +236,7 @@ module stage3_execute_stage ( // TODO: NEW always_ff @(posedge CLK, negedge nRST) begin if(!nRST) begin - ex_mem_if.ex_mem_reg <= '{default: '0}; + ex_mem_if.ex_mem_reg <= '{default: '0}; end else begin // TODO: This register is ~180b. Not awful, but can it be smaller? // PS: Does it even matter? Synth. tools may be able to merge regs. @@ -260,9 +259,11 @@ module stage3_execute_stage ( ex_mem_if.ex_mem_reg.csr_clr <= cu_if.csr_clr; ex_mem_if.ex_mem_reg.csr_set <= cu_if.csr_set; ex_mem_if.ex_mem_reg.csr_imm <= cu_if.csr_imm; + ex_mem_if.ex_mem_reg.csr_read_only <= (rf_if.rs1 == '0) || (cu_if.zimm == '0); ex_mem_if.ex_mem_reg.breakpoint <= cu_if.breakpoint; ex_mem_if.ex_mem_reg.ecall_insn <= cu_if.ecall_insn; ex_mem_if.ex_mem_reg.ret_insn <= cu_if.ret_insn; + ex_mem_if.ex_mem_reg.wfi_insn <= cu_if.wfi; ex_mem_if.ex_mem_reg.was_compressed <= 1'b0; // TODO: RV32C support end ex_mem_if.ex_mem_reg.illegal_insn <= cu_if.illegal_insn; diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv index 4e83b1a79..947a70724 100644 --- a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -81,7 +81,7 @@ module stage3_hazard_unit ( assign exception = hazard_if.fault_insn | hazard_if.mal_insn | prv_pipe_if.prot_fault_i | hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l | hazard_if.fault_s | hazard_if.mal_s | hazard_if.breakpoint - | hazard_if.env_m | prv_pipe_if.prot_fault_l | prv_pipe_if.prot_fault_s; + | hazard_if.env | prv_pipe_if.prot_fault_l | prv_pipe_if.prot_fault_s; assign intr = ~exception & prv_pipe_if.intr; @@ -116,7 +116,8 @@ module stage3_hazard_unit ( assign prv_pipe_if.fault_s = hazard_if.fault_s | prv_pipe_if.prot_fault_s; assign prv_pipe_if.mal_s = hazard_if.mal_s; assign prv_pipe_if.breakpoint = hazard_if.breakpoint; - assign prv_pipe_if.env_m = hazard_if.env_m; + assign prv_pipe_if.env = hazard_if.env; + assign prv_pipe_if.wfi = hazard_if.wfi; assign prv_pipe_if.ex_rmgmt = 1'b0;//rm_if.exception; assign prv_pipe_if.ex_rmgmt_cause = '0;//rm_if.ex_cause; @@ -156,7 +157,7 @@ module stage3_hazard_unit ( assign hazard_if.if_ex_flush = ex_flush_hazard // control hazard || branch_jump // control hazard || (wait_for_imem && !hazard_if.ex_mem_stall); // Flush if fetch stage lagging, but ex/mem are moving - + assign hazard_if.ex_mem_flush = ex_flush_hazard // Control hazard || branch_jump // Control hazard //|| (mem_use_stall && !hazard_if.d_mem_busy) // Data hazard -- flush once data memory is no longer busy (request complete) diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index 871958483..a7dddf542 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -112,14 +112,14 @@ module stage3_mem_stage( default: dgen_bus_if.wdata = '0; endcase end : STORE_TYPE - + // Endianness generate if(BUS_ENDIANNESS == "big") begin : g_data_bus_be assign byte_en = byte_en_temp; end else if(BUS_ENDIANNESS == "little") begin : g_data_bus_le assign byte_en = ex_mem_if.ex_mem_reg.dren ? byte_en_temp - : {byte_en_temp[0], byte_en_temp[1], + : {byte_en_temp[0], byte_en_temp[1], byte_en_temp[2], byte_en_temp[3]}; end endgenerate @@ -179,6 +179,7 @@ module stage3_mem_stage( assign prv_pipe_if.swap = ex_mem_if.ex_mem_reg.csr_swap; assign prv_pipe_if.clr = ex_mem_if.ex_mem_reg.csr_clr; assign prv_pipe_if.set = ex_mem_if.ex_mem_reg.csr_set; + assign prv_pipe_if.read_only = ex_mem_if.ex_mem_reg.csr_read_only; assign prv_pipe_if.wdata = ex_mem_if.ex_mem_reg.csr_imm ? {27'h0, ex_mem_if.ex_mem_reg.zimm} : ex_mem_if.ex_mem_reg.rs1_data; assign prv_pipe_if.csr_addr = ex_mem_if.ex_mem_reg.csr_addr; assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr @@ -187,21 +188,22 @@ module stage3_mem_stage( assign hazard_if.fault_insn = ex_mem_if.ex_mem_reg.fault_insn; assign hazard_if.mal_insn = ex_mem_if.ex_mem_reg.mal_insn; - assign hazard_if.illegal_insn = ex_mem_if.ex_mem_reg.illegal_insn; + assign hazard_if.illegal_insn = ex_mem_if.ex_mem_reg.illegal_insn || prv_pipe_if.invalid_priv_isn; assign hazard_if.fault_l = 1'b0; assign hazard_if.mal_l = ex_mem_if.ex_mem_reg.dren & mal_addr; assign hazard_if.fault_s = 1'b0; assign hazard_if.mal_s = ex_mem_if.ex_mem_reg.dwen & mal_addr; assign hazard_if.breakpoint = ex_mem_if.ex_mem_reg.breakpoint; - assign hazard_if.env_m = ex_mem_if.ex_mem_reg.ecall_insn; + assign hazard_if.env = ex_mem_if.ex_mem_reg.ecall_insn; assign hazard_if.ret = ex_mem_if.ex_mem_reg.ret_insn; + assign hazard_if.wfi = ex_mem_if.ex_mem_reg.wfi_insn; assign hazard_if.badaddr = (hazard_if.fault_insn || hazard_if.mal_insn) ? ex_mem_if.ex_mem_reg.badaddr : dgen_bus_if.addr; // NEW assign hazard_if.pc_m = ex_mem_if.ex_mem_reg.pc; assign hazard_if.valid_m = ex_mem_if.ex_mem_reg.valid; assign ex_mem_if.pc4 = ex_mem_if.ex_mem_reg.pc4; - + // Memory protection (doesn't consider RISC-MGMT) assign prv_pipe_if.dren = ex_mem_if.ex_mem_reg.dren; assign prv_pipe_if.dwen = ex_mem_if.ex_mem_reg.dwen; @@ -218,7 +220,7 @@ module stage3_mem_stage( assign ex_mem_if.rd_m = ex_mem_if.ex_mem_reg.rd_m; always_comb begin - // TODO: RISC-MGMT + // TODO: RISC-MGMT case (ex_mem_if.ex_mem_reg.w_sel) 3'd0: ex_mem_if.reg_wdata = dload_ext; 3'd1: ex_mem_if.reg_wdata = ex_mem_if.ex_mem_reg.pc4; @@ -247,7 +249,7 @@ module stage3_mem_stage( logic instr_30; // TODO: Fix up hazard unit - assign funct3 = ex_mem_if.ex_mem_reg.instr[14:12]; + assign funct3 = ex_mem_if.ex_mem_reg.instr[14:12]; assign funct12 = ex_mem_if.ex_mem_reg.instr[31:20]; assign instr_30 = ex_mem_if.ex_mem_reg.instr[30]; assign wb_stall = hazard_if.ex_mem_stall & ~hazard_if.jump & ~hazard_if.branch; // TODO: Is this right? diff --git a/source_code/pipelines/stage3/source/stage3_types_pkg.sv b/source_code/pipelines/stage3/source/stage3_types_pkg.sv index 4851a7dd2..e7ddd0603 100644 --- a/source_code/pipelines/stage3/source/stage3_types_pkg.sv +++ b/source_code/pipelines/stage3/source/stage3_types_pkg.sv @@ -41,9 +41,11 @@ package stage3_types_pkg; logic csr_clr; logic csr_set; logic csr_imm; + logic csr_read_only; logic breakpoint; logic ecall_insn; logic ret_insn; + logic wfi_insn; logic was_compressed; // Determine if PC should advance by 4 or 2, avoid passing PC and PC + (2/4) through pipeline logic [2:0] w_sel; logic [3:0] byte_en; // TODO: Where should this be generated? From cdd1e69841778b6db43d488aa9f0cf87ff48a536 Mon Sep 17 00:00:00 2001 From: Hadi Ahmed Date: Wed, 14 Dec 2022 17:43:25 -0500 Subject: [PATCH 31/47] Minor PMA fixes --- source_code/privs/priv_1_12/priv_1_12_pma.sv | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source_code/privs/priv_1_12/priv_1_12_pma.sv b/source_code/privs/priv_1_12/priv_1_12_pma.sv index e7a615514..aeb1d735b 100644 --- a/source_code/privs/priv_1_12/priv_1_12_pma.sv +++ b/source_code/privs/priv_1_12/priv_1_12_pma.sv @@ -46,6 +46,8 @@ module priv_1_12_pma ( `define RAM_PMA pma_cfg_t'({2'b0, 1'b1, 1'b1, 1'b1, WordAcc, 1'b1, 1'b1, 1'b1, RsrvEventual, AMONone, 1'b1}) // IO_PMA - reserved, W, R, X, WordAcc, no Idm, no Cache, Coh, RsrvEventual, AMONone, I/O `define IO_PMA pma_cfg_t'({2'b0, 1'b1, 1'b1, 1'b1, WordAcc, 1'b0, 1'b0, 1'b1, RsrvEventual, AMONone, 1'b0}) + // NONE_PMA - reserved, no W, no R, no X, WordAcc, no Idm, no Cache, no Coh, RsrvEventual, AMONone, Memory + `define NONE_PMA pma_cfg_t'({2'b0, 1'b0, 1'b0, 1'b0, WordAcc, 1'b0, 1'b0, 1'b0, RsrvNone, AMONone, 1'b1}) // Core State Registers always_ff @ (posedge CLK, negedge nRST) begin @@ -76,7 +78,7 @@ module priv_1_12_pma ( nxt_pma_regs = pma_regs; priv_ext_if.ack = 1'b0; new_val = pma_reg_t'(priv_ext_if.value_in); - if (priv_ext_if.csr_addr[11:4] == 8'b10111100) begin + if (priv_ext_if.csr_addr[11:4] == 8'hBC) begin priv_ext_if.ack = 1'b1; if (priv_ext_if.csr_active) begin // WARL checks @@ -95,6 +97,8 @@ module priv_1_12_pma ( nxt_pma_regs[priv_ext_if.csr_addr[3:0]] = new_val; end + end else if (priv_ext_if.csr_addr[11:4] == 8'hCC) begin + priv_ext_if.ack = 1'b1; end end From dd65e2d080a5f601037e53b238e7b0581f8be822 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Mon, 2 Jan 2023 14:46:33 -0500 Subject: [PATCH 32/47] stage3: Fix fetch-after-redirect bug Fixes a bug where I-fetch after a PC redirect could read the wrong instruction if the prior in-progress request became ready after the PC changed. Changes are: - Suppress iren whenever PC is redirected - Do not sample EPC from mem stage on interrupt (fixes repeated load/store instruction to non-idempotent region, but still permits load/store faults) - Expand memory controller ability to abort transactions when iren is suppressed --- .../pipelines/stage3/include/stage3_hazard_unit_if.vh | 2 +- source_code/pipelines/stage3/source/stage3_hazard_unit.sv | 7 ++++--- source_code/standard_core/memory_controller.sv | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh index 58b03f852..55ef37840 100644 --- a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh +++ b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh @@ -94,7 +94,7 @@ interface stage3_hazard_unit_if(); d_mem_busy, dren, dwen, ret, jump, branch, fence_stall, mispredict, halt, pc_m, valid_m, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env, - badaddr, ifence, + badaddr, ifence, wfi, token_mem ); diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv index 947a70724..2d73810b8 100644 --- a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -93,13 +93,14 @@ module stage3_hazard_unit ( assign hazard_if.iren = 1'b1; // TODO: Removed intr as cause of suppression -- is this OK? - assign hazard_if.suppress_iren = branch_jump || exception || prv_pipe_if.ret; // prevents a false instruction request from being sent when pipeline flush imminent + //assign hazard_if.suppress_iren = branch_jump || exception || prv_pipe_if.ret || prv_pipe_if.insert_pc; // prevents a false instruction request from being sent when pipeline flush imminent + assign hazard_if.suppress_iren = branch_jump || ex_flush_hazard || prv_pipe_if.ret || prv_pipe_if.insert_pc; // prevents a false instruction request from being sent when pipeline flush imminent assign hazard_if.suppress_data = exception; // suppress data transfer on interrupt/exception. Exception case: prevent read/write of faulting location. Interrupt: make symmetric with exceptions for ease assign hazard_if.rollback = (hazard_if.ifence && !hazard_if.fence_stall); // TODO: more cases for CSRs that affect I-fetch (PMA/PMP registers) // EPC priority logic - assign epc = hazard_if.valid_m ? hazard_if.pc_m : + assign epc = hazard_if.valid_m && !intr ? hazard_if.pc_m : (hazard_if.valid_e ? hazard_if.pc_e : hazard_if.pc_f); /* Send Exception notifications to Prv Block */ @@ -152,7 +153,7 @@ module stage3_hazard_unit ( || branch_jump || ex_flush_hazard || prv_pipe_if.insert_pc - || prv_pipe_if.ret; + || prv_pipe_if.ret;//) //&& !wait_for_imem; assign hazard_if.if_ex_flush = ex_flush_hazard // control hazard || branch_jump // control hazard diff --git a/source_code/standard_core/memory_controller.sv b/source_code/standard_core/memory_controller.sv index 3e80c9740..31bd807fb 100644 --- a/source_code/standard_core/memory_controller.sv +++ b/source_code/standard_core/memory_controller.sv @@ -109,6 +109,7 @@ module memory_controller ( if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = DATA_REQ; else next_state = IDLE; end else if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = INSTR_DATA_REQ; + else if(!i_gen_bus_if.ren) next_state = IDLE; else next_state = INSTR_WAIT; end From 22a355ad083add85dafd67de5c0e160df648d652 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Mon, 2 Jan 2023 14:53:35 -0500 Subject: [PATCH 33/47] APB: Fix back-to-back transactions This adds an extra state to the APB requester module to permit correct handling of back-to-back transactions. The new request state takes the same latched signals as the data state, so spurious input changes cannot break the request. --- source_code/bus_bridges/apb.sv | 41 +++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/source_code/bus_bridges/apb.sv b/source_code/bus_bridges/apb.sv index 53484fe67..cb197e2bd 100644 --- a/source_code/bus_bridges/apb.sv +++ b/source_code/bus_bridges/apb.sv @@ -5,8 +5,9 @@ module apb ( generic_bus_if.generic_bus out_gen_bus_if ); - typedef enum logic { + typedef enum logic [1:0] { IDLE, + REQUEST, DATA } state_t; @@ -43,23 +44,47 @@ module apb ( // TODO: How does APB work with the memory controller? always_comb begin + n_state = state; + if(state == IDLE && (out_gen_bus_if.ren || out_gen_bus_if.wen)) begin + n_state = REQUEST; + end else if(state == REQUEST) begin + n_state = DATA; + end else if(state == DATA && !apbif.PREADY) begin + n_state = DATA; + end else if(state == DATA && apbif.PREADY && (out_gen_bus_if.ren || out_gen_bus_if.wen)) begin + n_state = REQUEST; + end else if(state == DATA && apbif.PREADY && !(out_gen_bus_if.ren || out_gen_bus_if.wen)) begin + n_state = IDLE; + end + + /* if(state == DATA && !apbif.PREADY) begin n_state = state; - end else if(out_gen_bus_if.ren || out_gen_bus_if.wen) begin + end else if(state == DATA && apbif.PREADY && (out_gen_bus_if.ren || out_gen_bus_if.wen)) begin + n_state = REQUEST; + end else if(state == REQUEST) begin n_state = DATA; end else begin n_state = IDLE; end + */ end always_comb begin if(state == IDLE) begin - apbif.PADDR = out_gen_bus_if.addr; - apbif.PSEL = (out_gen_bus_if.ren || out_gen_bus_if.wen); + apbif.PADDR = '0; + apbif.PSEL = '0; apbif.PPROT = '0; - apbif.PENABLE = 0; - apbif.PWRITE = out_gen_bus_if.wen; - apbif.PSTRB = out_gen_bus_if.byte_en; + apbif.PENABLE = '0; + apbif.PWRITE = '0; + apbif.PSTRB = '0; + end else if(state == REQUEST) begin + apbif.PADDR = request.addr; + apbif.PSEL = 1'b1; + apbif.PPROT = '0; + apbif.PENABLE = 1'b0; + apbif.PWRITE = request.wen; + apbif.PSTRB = request.strobe; end else begin apbif.PADDR = request.addr; apbif.PSEL = 1'b1; @@ -72,7 +97,7 @@ module apb ( // Response assign out_gen_bus_if.rdata = apbif.PRDATA; - assign out_gen_bus_if.busy = (state == IDLE) || ~apbif.PREADY; + assign out_gen_bus_if.busy = (state == IDLE) || (state == REQUEST) || ~apbif.PREADY; assign apbif.PWDATA = out_gen_bus_if.wdata; endmodule From 7d63c0f11c287b44920843c7cea394716f5db447 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Mon, 2 Jan 2023 22:59:01 -0500 Subject: [PATCH 34/47] apb: Fix alignment Fixes an issue where misaligned addresses can appear on APB, causing completers to signal an error. Fix forces address alignment by tying lower bits to '0', and relying on strobe for writes. --- source_code/bus_bridges/apb.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_code/bus_bridges/apb.sv b/source_code/bus_bridges/apb.sv index cb197e2bd..63ee7f9f0 100644 --- a/source_code/bus_bridges/apb.sv +++ b/source_code/bus_bridges/apb.sv @@ -34,7 +34,7 @@ module apb ( always_comb begin request_n = request; if(state == IDLE || (state == DATA && apbif.PREADY)) begin - request_n.addr = out_gen_bus_if.addr; + request_n.addr = {out_gen_bus_if.addr[31:2], 2'b00}; request_n.wdata = out_gen_bus_if.wdata; request_n.wen = out_gen_bus_if.wen; request_n.strobe = out_gen_bus_if.byte_en; From 4153d134fa4932e51947eca2a660ae085969a066 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Tue, 3 Jan 2023 23:28:16 -0500 Subject: [PATCH 35/47] ram_sim_model: Change to support loading binary data instead of hex This commit fixes up the SystemVerilog self-test testbench, and changes the simulated ram model to use binary files to match the behavior of the Verilator testbench. Only basic testing has been done. --- source_code/ram/ram_sim_model.sv | 34 ++++++++++++++++++-- source_code/tb/tb_RISCVBusiness_self_test.sv | 9 +++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/source_code/ram/ram_sim_model.sv b/source_code/ram/ram_sim_model.sv index 3b644fae4..dfec14a45 100644 --- a/source_code/ram/ram_sim_model.sv +++ b/source_code/ram/ram_sim_model.sv @@ -28,7 +28,7 @@ module ram_sim_model #( parameter ENDIANNESS = "little", parameter N_BYTES = 4, parameter DEPTH = 8192, - parameter MEM_INIT_FILE = "meminit.hex", + parameter MEM_INIT_FILE = "meminit.bin", parameter MEM_DEFAULT = 32'h0000_0000, parameter ADDR_BITS = $clog2(DEPTH), parameter COUNT_BITS = $clog2(LAT) + 1, @@ -71,9 +71,13 @@ module ram_sim_model #( string line; int res; + logic [7:0] byte_buf; + logic [31:0] word_buf; + int bytes_read; + // Load in meminit - initial begin + /*initial begin if (MEM_INIT_FILE != "") begin fptr = $fopen(MEM_INIT_FILE, "r"); if (!fptr) begin @@ -92,6 +96,32 @@ module ram_sim_model #( $fclose(fptr); end end + end*/ + + // assumes binary file starts at bottom of memory! + initial begin + if(MEM_INIT_FILE != "") begin + fptr = $fopen(MEM_INIT_FILE, "r"); + if(!fptr) begin + $info("Warning: Couldn't open memory init file %s", MEM_INIT_FILE); + end else begin + faddr = 32'h8000_0000; + while(!$feof(fptr)) begin + word_buf = 32'b0; + for(int i = 0; i < 4; i++) begin + bytes_read = $fread(byte_buf, fptr); + if(bytes_read < 1) begin + word_buf <<= (3-i)*8; + break; + end else begin + word_buf = {word_buf[23:0], byte_buf}; + end + end + memory[faddr] = word_buf; + faddr += 1; + end + end + end end /* diff --git a/source_code/tb/tb_RISCVBusiness_self_test.sv b/source_code/tb/tb_RISCVBusiness_self_test.sv index 596aa9258..04588fcbd 100644 --- a/source_code/tb/tb_RISCVBusiness_self_test.sv +++ b/source_code/tb/tb_RISCVBusiness_self_test.sv @@ -80,7 +80,8 @@ module tb_RISCVBusiness_self_test (); assign data = data_temp; else ;//TODO:ERROR - bind tspp_execute_stage cpu_tracker cpu_track1 ( + /* + bind stage3_execute_stage cpu_tracker cpu_track1 ( .CLK(CLK), .wb_stall(wb_stall), .instr(fetch_ex_if.fetch_ex_reg.instr), @@ -105,7 +106,7 @@ module tb_RISCVBusiness_self_test (); .update_predictor(predict_if.update_predictor), .prediction(predict_if.prediction), .branch_result(predict_if.branch_result) - ); + );*/ //Ramif Mux always_comb begin @@ -167,9 +168,9 @@ module tb_RISCVBusiness_self_test (); // Check Register 28 to see if test passed or failed if (clk_count == `RVBSELF_CLK_TIMEOUT) $display("ERROR: Test timed out"); - else if(DUT.execute_stage_i.g_rfile_select.rf.registers[28] != 32'h1) + else if(DUT.pipeline.execute_stage_i.g_rfile_select.rf.registers[28] != 32'h1) $display("ERROR: Test %0d did not pass", - (DUT.execute_stage_i.g_rfile_select.rf.registers[28] - 1)/2); + (DUT.pipeline.execute_stage_i.g_rfile_select.rf.registers[28] - 1)/2); else $display("SUCCESS"); $finish; From 2be5971f3d8d35e67dd0a90fa28599a5236f1a3f Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Tue, 3 Jan 2023 23:31:16 -0500 Subject: [PATCH 36/47] stage3: Fix bugs preventing runs with Xcelium/Modelsim Connect some unconnected signals (masked by Verilator 'x' handling) and fix some parsing differences between Verilator and Xcelium/Modelsim. --- .../pipelines/stage3/source/stage3_execute_stage.sv | 12 ++++++------ .../pipelines/stage3/source/stage3_mem_stage.sv | 1 + source_code/rv32m/rv32m_decode.sv | 5 +++-- source_code/rv32m/rv32m_enabled.sv | 6 +++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index 1bd3967de..203a0b988 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -71,11 +71,11 @@ module stage3_execute_stage ( control_unit cu ( .cu_if(cu_if), .rf_if(rf_if), - .rmgmt_rsel_s_0(), - .rmgmt_rsel_s_1(), - .rmgmt_rsel_d(), - .rmgmt_req_reg_r(), - .rmgmt_req_reg_w() + .rmgmt_rsel_s_0('0), + .rmgmt_rsel_s_1('0), + .rmgmt_rsel_d('0), + .rmgmt_req_reg_r('0), + .rmgmt_req_reg_w('0) //.rmgmt_rsel_s_0(rm_if.rsel_s_0), //.rmgmt_rsel_s_1(rm_if.rsel_s_1), //.rmgmt_rsel_d(rm_if.rsel_d), @@ -108,6 +108,7 @@ module stage3_execute_stage ( logic rv32m_busy; word_t rv32m_out; word_t ex_out; + word_t rs1_post_fwd, rs2_post_fwd; alu alu (.*); jump_calc jump_calc (.*); @@ -126,7 +127,6 @@ module stage3_execute_stage ( // Forwarding // These rs*_post_fwd values should be used in place of rs1/rs2 anywhere they are used - word_t rs1_post_fwd, rs2_post_fwd; assign rs1_post_fwd = fw_if.fwd_rs1 ? fw_if.rd_mem_data : rf_if.rs1_data; assign rs2_post_fwd = fw_if.fwd_rs2 ? fw_if.rd_mem_data : rf_if.rs2_data; diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index a7dddf542..a3dc6bd8a 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -171,6 +171,7 @@ module stage3_mem_stage( assign halt = ex_mem_if.ex_mem_reg.halt; assign fw_if.rd_m = ex_mem_if.ex_mem_reg.rd_m; assign fw_if.reg_write = ex_mem_if.reg_write; + assign fw_if.load = (ex_mem_if.ex_mem_reg.dren || ex_mem_if.ex_mem_reg.dwen); /****** diff --git a/source_code/rv32m/rv32m_decode.sv b/source_code/rv32m/rv32m_decode.sv index 802d1995a..afeb939ff 100644 --- a/source_code/rv32m/rv32m_decode.sv +++ b/source_code/rv32m/rv32m_decode.sv @@ -29,8 +29,9 @@ module rv32m_decode ( import rv32m_pkg::*; - rv32m_insn_t insn_split = rv32m_insn_t'(insn); - + rv32m_insn_t insn_split; + + assign insn_split = rv32m_insn_t'(insn); assign claim = (insn_split.opcode_major == RV32M_OPCODE) && (insn_split.opcode_minor == RV32M_OPCODE_MINOR); diff --git a/source_code/rv32m/rv32m_enabled.sv b/source_code/rv32m/rv32m_enabled.sv index 6d1004f53..d61f13db8 100644 --- a/source_code/rv32m/rv32m_enabled.sv +++ b/source_code/rv32m/rv32m_enabled.sv @@ -30,8 +30,8 @@ module rv32m_enabled ( input rv32m_pkg::rv32m_op_t operation, input [31:0] rv32m_a, input [31:0] rv32m_b, - output rv32m_busy, - output [31:0] rv32m_out + output logic rv32m_busy, + output logic [31:0] rv32m_out ); import rv32m_pkg::*; @@ -72,7 +72,7 @@ module rv32m_enabled ( op_a_save <= '0; op_b_save <= '0; //is_signed_save <= '0; - operation_save <= '0; + operation_save <= MUL; end else if (operand_diff) begin op_a_save <= rv32m_a; op_b_save <= rv32m_b; From 435c231e003fcd4f5721eceb32f590ee6500b7e3 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Mon, 9 Jan 2023 16:18:40 -0500 Subject: [PATCH 37/47] stage3: Fix bug where MEPC gets unknown value The signal "valid_e" was unassigned, causing an unknown value in simulation with Xcelium --- source_code/pipelines/stage3/source/stage3_execute_stage.sv | 1 + 1 file changed, 1 insertion(+) diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index 203a0b988..0084dd15d 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -231,6 +231,7 @@ module stage3_execute_stage ( assign hazard_if.pc_e = fetch_ex_if.fetch_ex_reg.pc; assign hazard_if.ex_busy = rv32m_busy; // Add & conditions here for other FUs that can stall + assign hazard_if.valid_e = fetch_ex_if.fetch_ex_reg.valid; // TODO: NEW From 2c8f46ce22676bed398ae66b7026147f3fe2ce9f Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Mon, 9 Jan 2023 16:19:45 -0500 Subject: [PATCH 38/47] bus_bridges: Fixup AHB bridge for AFTx07 --- source_code/bus_bridges/ahb.sv | 18 ++++++++++-------- source_code/standard_core/RISCVBusiness.sv | 4 ++-- source_code/standard_core/memory_controller.sv | 14 +++++++++----- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/source_code/bus_bridges/ahb.sv b/source_code/bus_bridges/ahb.sv index 8263a16f6..1a8e59b5e 100644 --- a/source_code/bus_bridges/ahb.sv +++ b/source_code/bus_bridges/ahb.sv @@ -60,33 +60,35 @@ module ahb ( if (out_gen_bus_if.ren) begin ahb_m.HTRANS = 2'b10; ahb_m.HWRITE = 1'b0; - ahb_m.HADDR = out_gen_bus_if.addr; + ahb_m.HADDR = {out_gen_bus_if.addr[31:2], 2'b00}; ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HMASTLOCK = 0; + ahb_m.HWSTRB = out_gen_bus_if.byte_en; end else if (out_gen_bus_if.wen) begin ahb_m.HTRANS = 2'b10; ahb_m.HWRITE = 1'b1; - ahb_m.HADDR = out_gen_bus_if.addr; + ahb_m.HADDR = {out_gen_bus_if.addr[31:2], 2'b00}; ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HMASTLOCK = 0; + ahb_m.HWSTRB = out_gen_bus_if.byte_en; end else begin ahb_m.HTRANS = 2'b0; ahb_m.HWRITE = 1'b0; ahb_m.HADDR = 0; ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HMASTLOCK = 0; + ahb_m.HWSTRB = out_gen_bus_if.byte_en; end if (state == DATA) begin ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HWSTRB = out_gen_bus_if.byte_en; end end assign out_gen_bus_if.busy = state == IDLE || ~((ahb_m.HREADY && (state == DATA))); assign out_gen_bus_if.rdata = ahb_m.HRDATA; + // Unused signals + assign ahb_m.HMASTLOCK = 1'b0; + assign ahb_m.HBURST = 3'b000; + assign ahb_m.HSEL = (ahb_m.HTRANS != 2'b00); endmodule diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index 6c8eb7104..ef0f7b1d1 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -41,7 +41,7 @@ module RISCVBusiness ( `ifdef BUS_INTERFACE_GENERIC_BUS generic_bus_if.cpu gen_bus_if `elsif BUS_INTERFACE_AHB - ahb_if.manager ahb_master + ahb_if.manager ahb_manager `elsif BUS_INTERFACE_APB apb_if.requester apb_requester `endif @@ -216,7 +216,7 @@ module RISCVBusiness ( .CLK(CLK), .nRST(nRST), .out_gen_bus_if(pipeline_trans_if), - .ahb_m(ahb_master) + .ahb_m(ahb_manager) ); end "apb_if": begin : g_apb_if diff --git a/source_code/standard_core/memory_controller.sv b/source_code/standard_core/memory_controller.sv index 31bd807fb..31d34686f 100644 --- a/source_code/standard_core/memory_controller.sv +++ b/source_code/standard_core/memory_controller.sv @@ -138,6 +138,7 @@ module memory_controller ( end //-- INSTRUCTION REQUEST --// + // Byte enable is relevant to data phase INSTR_REQ: begin out_gen_bus_if.wen = i_gen_bus_if.wen; out_gen_bus_if.ren = i_gen_bus_if.ren; @@ -150,7 +151,7 @@ module memory_controller ( out_gen_bus_if.wen = d_gen_bus_if.wen; out_gen_bus_if.ren = d_gen_bus_if.ren; out_gen_bus_if.addr = d_gen_bus_if.addr; - out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; i_gen_bus_if.busy = out_gen_bus_if.busy; d_gen_bus_if.busy = 1'b1; end @@ -176,14 +177,17 @@ module memory_controller ( out_gen_bus_if.wen = i_gen_bus_if.wen; out_gen_bus_if.ren = i_gen_bus_if.ren; out_gen_bus_if.addr = i_gen_bus_if.addr; - out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; d_gen_bus_if.busy = out_gen_bus_if.busy; i_gen_bus_if.busy = 1'b1; end DATA_WAIT: begin - out_gen_bus_if.wen = d_gen_bus_if.wen; - out_gen_bus_if.ren = d_gen_bus_if.ren; - out_gen_bus_if.addr = d_gen_bus_if.addr; + //out_gen_bus_if.wen = d_gen_bus_if.wen; + //out_gen_bus_if.ren = d_gen_bus_if.ren; + //out_gen_bus_if.addr = d_gen_bus_if.addr; + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; i_gen_bus_if.busy = 1'b1; d_gen_bus_if.busy = out_gen_bus_if.busy; From f9c4eb91a4fab4191545064dea2f4cfac0e1c271 Mon Sep 17 00:00:00 2001 From: Jimmy Mingze Jin <77997752+ttlapik@users.noreply.github.com> Date: Mon, 24 Apr 2023 13:52:33 -0400 Subject: [PATCH 39/47] l1 caches integration (#24) L1 Cache integration --------- Co-authored-by: Jimmy Co-authored-by: Cole Nelson --- .gitignore | 20 + example.yml | 11 +- run_tests_verilator.py | 2 +- scripts/config_core.py | 36 +- source_code/caches/cache_if.svh | 49 +++ source_code/caches/caches.core | 2 + source_code/caches/l1/l1_cache.sv | 416 ++++++++++++++++++ source_code/caches/memory_arbiter.sv | 100 +++++ source_code/caches/separate_caches.sv | 34 ++ source_code/caches/sram/run_sram_test.sh | 2 + source_code/caches/sram/sram.sv | 54 +++ source_code/caches/sram/sram_tb.sv | 96 ++++ .../stage3/source/stage3_execute_stage.sv | 2 +- .../stage3/source/stage3_fetch_stage.sv | 2 +- .../stage3/source/stage3_hazard_unit.sv | 5 +- .../stage3/source/stage3_mem_stage.sv | 20 +- source_code/trackers/cpu_tracker.sv | 50 ++- verification/u-mode/csr.c | 5 +- verification/uvm/caches/bfm/memory_bfm.svh | 174 ++++++++ .../uvm/caches/bus_agents/l2_agent.svh | 52 +++ .../uvm/caches/bus_agents/mem_agent.svh | 51 +++ .../uvm/caches/bus_agents/mem_arb_agent.svh | 72 +++ verification/uvm/caches/coverage/.gitignore | 4 + .../uvm/caches/cpu_agent/cpu_driver.svh | 124 ++++++ .../uvm/caches/cpu_agent/cpu_sequencer.svh | 32 ++ .../uvm/caches/cpu_agent/cpu_transaction.svh | 69 +++ .../uvm/caches/cpu_agent/d_cpu_agent.svh | 73 +++ .../uvm/caches/cpu_agent/i_cpu_agent.svh | 73 +++ verification/uvm/caches/end2end/end2end.svh | 233 ++++++++++ verification/uvm/caches/env/cache_env.svh | 237 ++++++++++ .../uvm/caches/env/cache_env_config.svh | 79 ++++ verification/uvm/caches/env/dut_params.svh | 59 +++ .../uvm/caches/env/interface_checker.svh | 74 ++++ .../generic_bus_agent_comps/bus_agent.svh | 55 +++ .../generic_bus_agent_comps/bus_monitor.svh | 168 +++++++ .../generic_bus_agent_comps/bus_predictor.svh | 106 +++++ .../bus_scoreboard.svh | 97 ++++ verification/uvm/caches/models/Utils.svh | 43 ++ .../uvm/caches/models/cache_model.svh | 240 ++++++++++ verification/uvm/caches/readme.md | 89 ++++ verification/uvm/caches/run.py | 132 ++++++ verification/uvm/caches/scripts/__init__.py | 24 + verification/uvm/caches/scripts/build.py | 74 ++++ verification/uvm/caches/scripts/cprint.py | 54 +++ verification/uvm/caches/scripts/post_run.py | 130 ++++++ verification/uvm/caches/scripts/repeat.py | 42 ++ verification/uvm/caches/scripts/run.do | 4 + verification/uvm/caches/scripts/run.py | 80 ++++ .../uvm/caches/sequences/base_sequence.svh | 45 ++ .../uvm/caches/sequences/evict_sequence.svh | 83 ++++ .../uvm/caches/sequences/flush_sequence.svh | 93 ++++ .../uvm/caches/sequences/index_sequence.svh | 98 +++++ .../uvm/caches/sequences/master_sequence.svh | 174 ++++++++ .../uvm/caches/sequences/mmio_sequence.svh | 66 +++ .../uvm/caches/sequences/nominal_sequence.svh | 91 ++++ verification/uvm/caches/tb_caches_top.sv | 217 +++++++++ verification/uvm/caches/testplan/testplan.ods | Bin 0 -> 14739 bytes verification/uvm/caches/tests/base_test.svh | 174 ++++++++ verification/uvm/caches/tests/evict_test.svh | 41 ++ verification/uvm/caches/tests/flush_test.svh | 45 ++ verification/uvm/caches/tests/index_test.svh | 42 ++ verification/uvm/caches/tests/mmio_test.svh | 45 ++ .../uvm/caches/tests/nominal_test.svh | 47 ++ verification/uvm/caches/tests/random_test.svh | 44 ++ verification/uvm/caches/waves/full.do | 206 +++++++++ verification/uvm/caches/waves/l1.do | 88 ++++ verification/uvm/caches/waves/l2.do | 88 ++++ wave_format.gtkw | 147 ------- 68 files changed, 5304 insertions(+), 180 deletions(-) create mode 100644 source_code/caches/cache_if.svh create mode 100644 source_code/caches/l1/l1_cache.sv create mode 100644 source_code/caches/memory_arbiter.sv create mode 100755 source_code/caches/sram/run_sram_test.sh create mode 100644 source_code/caches/sram/sram.sv create mode 100644 source_code/caches/sram/sram_tb.sv create mode 100644 verification/uvm/caches/bfm/memory_bfm.svh create mode 100644 verification/uvm/caches/bus_agents/l2_agent.svh create mode 100644 verification/uvm/caches/bus_agents/mem_agent.svh create mode 100644 verification/uvm/caches/bus_agents/mem_arb_agent.svh create mode 100644 verification/uvm/caches/coverage/.gitignore create mode 100644 verification/uvm/caches/cpu_agent/cpu_driver.svh create mode 100644 verification/uvm/caches/cpu_agent/cpu_sequencer.svh create mode 100644 verification/uvm/caches/cpu_agent/cpu_transaction.svh create mode 100644 verification/uvm/caches/cpu_agent/d_cpu_agent.svh create mode 100644 verification/uvm/caches/cpu_agent/i_cpu_agent.svh create mode 100644 verification/uvm/caches/end2end/end2end.svh create mode 100644 verification/uvm/caches/env/cache_env.svh create mode 100644 verification/uvm/caches/env/cache_env_config.svh create mode 100644 verification/uvm/caches/env/dut_params.svh create mode 100644 verification/uvm/caches/env/interface_checker.svh create mode 100644 verification/uvm/caches/generic_bus_agent_comps/bus_agent.svh create mode 100644 verification/uvm/caches/generic_bus_agent_comps/bus_monitor.svh create mode 100644 verification/uvm/caches/generic_bus_agent_comps/bus_predictor.svh create mode 100644 verification/uvm/caches/generic_bus_agent_comps/bus_scoreboard.svh create mode 100644 verification/uvm/caches/models/Utils.svh create mode 100644 verification/uvm/caches/models/cache_model.svh create mode 100644 verification/uvm/caches/readme.md create mode 100755 verification/uvm/caches/run.py create mode 100644 verification/uvm/caches/scripts/__init__.py create mode 100644 verification/uvm/caches/scripts/build.py create mode 100644 verification/uvm/caches/scripts/cprint.py create mode 100644 verification/uvm/caches/scripts/post_run.py create mode 100644 verification/uvm/caches/scripts/repeat.py create mode 100644 verification/uvm/caches/scripts/run.do create mode 100644 verification/uvm/caches/scripts/run.py create mode 100644 verification/uvm/caches/sequences/base_sequence.svh create mode 100644 verification/uvm/caches/sequences/evict_sequence.svh create mode 100644 verification/uvm/caches/sequences/flush_sequence.svh create mode 100644 verification/uvm/caches/sequences/index_sequence.svh create mode 100644 verification/uvm/caches/sequences/master_sequence.svh create mode 100644 verification/uvm/caches/sequences/mmio_sequence.svh create mode 100644 verification/uvm/caches/sequences/nominal_sequence.svh create mode 100644 verification/uvm/caches/tb_caches_top.sv create mode 100644 verification/uvm/caches/testplan/testplan.ods create mode 100644 verification/uvm/caches/tests/base_test.svh create mode 100644 verification/uvm/caches/tests/evict_test.svh create mode 100644 verification/uvm/caches/tests/flush_test.svh create mode 100644 verification/uvm/caches/tests/index_test.svh create mode 100644 verification/uvm/caches/tests/mmio_test.svh create mode 100644 verification/uvm/caches/tests/nominal_test.svh create mode 100644 verification/uvm/caches/tests/random_test.svh create mode 100644 verification/uvm/caches/waves/full.do create mode 100644 verification/uvm/caches/waves/l1.do create mode 100644 verification/uvm/caches/waves/l2.do delete mode 100644 wave_format.gtkw diff --git a/.gitignore b/.gitignore index 8f490c0e0..52703ebfe 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,23 @@ fusesoc.conf # synthesis scripts desflow1/ .vscode/ + +# uvm +work +mapped +fpga +transcript +*.wlf +README +*.swp +._* +*.log +*.deps +*.hex +*.ver +mti* +*.diff +system.summary +system_fpga.summary +*.vstf +*vsim* \ No newline at end of file diff --git a/example.yml b/example.yml index 8d153ea28..bd565a31e 100644 --- a/example.yml +++ b/example.yml @@ -9,8 +9,15 @@ microarch_params: # Cache configurations cache_config : "separate" - dcache_type : "pass_through" - icache_type : "pass_through" + dcache_type : "l1" + dcache_size : 1024 + dcache_block_size : 2 + dcache_assoc : 1 + icache_type : "l1" + icache_size : 1024 + icache_block_size : 2 + icache_assoc : 1 + noncache_start_addr : 32'hF000_0000 # Bus configurations bus_endianness : "big" diff --git a/run_tests_verilator.py b/run_tests_verilator.py index 28073aa56..81d90cf83 100755 --- a/run_tests_verilator.py +++ b/run_tests_verilator.py @@ -161,7 +161,7 @@ def run_tests(config: Type[run_config]) -> List[str]: # cache the result in the dict test_status[filepath.stem] = result - #print(test_status) + # print(test_status) # catch keyboard interrupts to flush the cache file except KeyboardInterrupt: pass # yes I know that this is not the pest practice diff --git a/scripts/config_core.py b/scripts/config_core.py index a0f92ef21..3019a61ff 100755 --- a/scripts/config_core.py +++ b/scripts/config_core.py @@ -40,8 +40,17 @@ 'br_predictor_type' : ['not_taken'], # Cache Configurations 'cache_config' : ['separate'], - 'dcache_type' : ['pass_through', 'direct_mapped_tpf'], - 'icache_type' : ['pass_through', 'direct_mapped_tpf'], + 'dcache_type' : ['pass_through', 'direct_mapped_tpf', 'l1'], + 'icache_type' : ['pass_through', 'direct_mapped_tpf', 'l1'], + # Cache Configurations (free_params) + 'noncache_start_addr' : [], + # Cache Configurations (int_params) + 'dcache_size' : [], + 'dcache_block_size' : [], + 'dcache_assoc' : [], + 'icache_size' : [], + 'icache_block_size' : [], + 'icache_assoc' : [], # Bus Configurations 'bus_endianness' : ['big', 'little'], 'bus_interface_type' : ['ahb_if', 'generic_bus_if', 'apb_if'], @@ -115,6 +124,8 @@ def create_include(config): # Handle localparam configurations isa_params = config['isa_params'] + free_params = ['noncache_start_addr'] + int_params = ['dcache_size', 'dcache_block_size', 'dcache_assoc', 'icache_size', 'icache_block_size', 'icache_assoc'] include_file.write('// ISA Params:\n') for isa_param in isa_params: try: @@ -136,13 +147,30 @@ def create_include(config): include_file.write('\n// Microarch Params:\n') uarch_params = config['microarch_params'] for uarch_param in uarch_params: - if uarch_params[uarch_param] not in UARCH_PARAMS[uarch_param]: + # errors + if uarch_param in int_params or uarch_param in free_params: + if uarch_param not in free_params and not isinstance(uarch_params[uarch_param], int): + err = 'Illegal configuration of incorrect type for ' + uarch_param + sys.exit(err) + if uarch_params['dcache_size'] % (uarch_params['dcache_block_size'] * uarch_params['dcache_assoc']) is not 0: + err = 'Invalid dcache_size. Not divisible by block_size * assoc.' + sys.exit(err) + if uarch_params['icache_size'] % (uarch_params['icache_block_size'] * uarch_params['icache_assoc']) is not 0: + err = 'Invalid icache_size. Not divisible by block_size * assoc.' + sys.exit(err) + elif uarch_params[uarch_param] not in UARCH_PARAMS[uarch_param]: err = 'Illegal configuration. ' + uarch_params[uarch_param] err += ' is not a valid configuration for ' + uarch_param sys.exit(err) + # write to parameter file + if uarch_param in free_params or uarch_param in int_params: + line = 'localparam ' + uarch_param.upper() + ' = ' + str(uarch_params[uarch_param]) + ';\n' else: line = 'localparam ' - line += uarch_param.upper() + ' = "' + uarch_params[uarch_param] + '"' + if isinstance(uarch_params[uarch_param], str): # deal with integer params + line += uarch_param.upper() + ' = "' + uarch_params[uarch_param] + '"' + else: + line += uarch_param.upper() + ' = ' + str(uarch_params[uarch_param]) line += ';\n' include_file.write(line) diff --git a/source_code/caches/cache_if.svh b/source_code/caches/cache_if.svh new file mode 100644 index 000000000..b0481da12 --- /dev/null +++ b/source_code/caches/cache_if.svh @@ -0,0 +1,49 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cache_if.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/03/2022 +* Description: Bundles cache signals into one interface for each of passing in UVM DB +*/ + +`ifndef CACHE_IF_SVH +`define CACHE_IF_SVH + +interface cache_if( + input logic CLK +); + logic nRST; + logic clear, flush; + logic clear_done, flush_done; + + modport driver + ( + output nRST, + output clear, flush, + input CLK, clear_done, flush_done + ); + + modport cache + ( + input clear, flush, CLK, nRST, + output clear_done, flush_done + ); +endinterface + +`endif diff --git a/source_code/caches/caches.core b/source_code/caches/caches.core index 5e4f616e2..03c318ab2 100644 --- a/source_code/caches/caches.core +++ b/source_code/caches/caches.core @@ -11,6 +11,8 @@ filesets: - separate_caches.sv - direct_mapped_tpf/direct_mapped_tpf_cache.sv - pass_through/pass_through_cache.sv + - l1/l1_cache.sv + - sram/sram.sv file_type: systemVerilogSource targets: diff --git a/source_code/caches/l1/l1_cache.sv b/source_code/caches/l1/l1_cache.sv new file mode 100644 index 000000000..293260ff7 --- /dev/null +++ b/source_code/caches/l1/l1_cache.sv @@ -0,0 +1,416 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: l1_cache.sv +* +* Created by: Rufat Imanov, Aedan Frazier, Dhruv Gupta +* Email: rimanov@purdue.edu +* Date Created: 06/20/2021 +* Description: L1 Cache. The following are configurable: +* - Cache Size +* - Non-Cacheable start address +* - Block Size | max 8 +* - ASSOC | either 1 or 2 +*/ + +`include "generic_bus_if.vh" + +module l1_cache #( + parameter CACHE_SIZE = 1024, // must be power of 2, in bytes, max 4k - 4 * 2^10 + parameter BLOCK_SIZE = 2, // must be power of 2, max 8 + parameter ASSOC = 1, // dont set this to 0 + parameter NONCACHE_START_ADDR = 32'hF000_0000 // sh/sb still have issues when uncached; not sure whats up with that still tbh +) +( + input logic CLK, nRST, + input logic clear, flush, + output logic clear_done, flush_done, + generic_bus_if.cpu mem_gen_bus_if, + generic_bus_if.generic_bus proc_gen_bus_if +); + import rv32i_types_pkg::*; + + // local parameters + localparam N_TOTAL_BYTES = CACHE_SIZE / 8; + localparam N_TOTAL_WORDS = N_TOTAL_BYTES / 4; + localparam N_TOTAL_FRAMES = N_TOTAL_WORDS / BLOCK_SIZE; + localparam N_SETS = N_TOTAL_FRAMES / ASSOC; + localparam N_FRAME_BITS = $clog2(ASSOC) + (ASSOC == 1); + localparam N_SET_BITS = $clog2(N_SETS) + (N_SETS == 1); + localparam N_BLOCK_BITS = $clog2(BLOCK_SIZE) + (BLOCK_SIZE == 1); + localparam N_TAG_BITS = WORD_SIZE - N_SET_BITS - N_BLOCK_BITS - 2; + localparam FRAME_SIZE = WORD_SIZE * BLOCK_SIZE + N_TAG_BITS + 2; // in bits + localparam SRAM_W = FRAME_SIZE * ASSOC; // sram parameters + + typedef struct packed { + logic valid; + logic dirty; + logic [N_TAG_BITS - 1:0] tag; + word_t [BLOCK_SIZE - 1:0] data; + } cache_frame_t; // cache frame + + typedef struct packed { + cache_frame_t [ASSOC - 1:0] frames; + } cache_set_t; // cache set + + typedef struct packed { + logic [N_TAG_BITS-1:0] tag_bits; + logic [N_SET_BITS-1:0] idx_bits; + logic [N_BLOCK_BITS-1:0] block_bits; + logic [1:0] byte_bits; + } decoded_cache_addr_t; // cache address type + + typedef struct packed { + logic finish; + logic [N_SET_BITS-1:0] set_num; + logic [N_FRAME_BITS-1:0] frame_num; // assoc + logic [N_BLOCK_BITS-1:0] word_num; + } flush_idx_t; // flush counter type + + typedef enum { + IDLE, HIT, FETCH, WB, FLUSH_CACHE + } cache_fsm_t; // cache state machine + + // counter signals + flush_idx_t flush_idx, next_flush_idx; + logic [N_BLOCK_BITS:0] word_num, next_word_num; + logic enable_word_count, clear_word_count, + clear_flush_count, enable_flush_count, enable_flush_count_nowb; + logic word_count_done; + // States + cache_fsm_t state, next_state; + // lru + logic [N_FRAME_BITS-1:0] ridx; + logic [N_SETS-1:0] last_used; + logic [N_SETS-1:0] next_last_used; + // address + word_t read_addr, next_read_addr; + decoded_cache_addr_t decoded_req_addr, next_decoded_req_addr; + decoded_cache_addr_t decoded_addr; + // Cache Hit + logic hit, pass_through; + word_t [BLOCK_SIZE-1:0] hit_data; + logic [N_FRAME_BITS-1:0] hit_idx; + // sram signals + cache_set_t sramWrite, sramRead, sramMask; + logic sramWEN; // no need for REN + logic [N_SET_BITS-1:0] sramSEL; + // flush reg + logic flush_req, nflush_req; + logic idle_done; + + // sram instance + assign sramSEL = (state == FLUSH_CACHE || state == IDLE) ? flush_idx.set_num : decoded_addr.idx_bits; + sram #(.SRAM_WR_SIZE(SRAM_W), .SRAM_HEIGHT(N_SETS)) + SRAM(CLK, nRST, sramWrite, sramRead, 1'b1, sramWEN, sramSEL, sramMask); + + // flip flops + always_ff @ (posedge CLK, negedge nRST) begin + if(~nRST) begin + state <= IDLE; + flush_idx <= 0; + word_num <= 0; + last_used <= 0; + read_addr <= 0; + decoded_req_addr <= 0; + flush_req <= 0; + end + else begin + state <= next_state; // cache state machine + flush_idx <= next_flush_idx; // index for flushing the cache entries + word_num <= next_word_num; // word counter for fetching/writing back + last_used <= next_last_used; // MRU index + read_addr <= next_read_addr; // cache address to provide to memory + decoded_req_addr <= next_decoded_req_addr; // cache address requested by core + flush_req <= nflush_req; // flush requested by core + end + end + + // counters + always_comb begin + next_word_num = word_num; + next_flush_idx = flush_idx; + word_count_done = ~mem_gen_bus_if.busy && (BLOCK_SIZE - 1) == word_num; + // word counter logic + if (clear_word_count) + next_word_num = 0; + else if (enable_word_count) + next_word_num = word_num + 1; + + // flush counter logic + if (clear_flush_count) + next_flush_idx = 0; + else if (enable_flush_count_nowb) + next_flush_idx = flush_idx + BLOCK_SIZE; + else if (enable_flush_count) + next_flush_idx = flush_idx + 1; + + // correction for non-powers of 2 or 1 + if (next_flush_idx.set_num == N_SETS) + next_flush_idx = {1'b1, (N_SET_BITS + N_FRAME_BITS + N_BLOCK_BITS)'('0)}; + if (next_flush_idx.frame_num == ASSOC) + next_flush_idx = {({flush_idx.finish, flush_idx.set_num} + 1'b1), (N_FRAME_BITS + N_BLOCK_BITS)'('0)}; + if (next_flush_idx.word_num == BLOCK_SIZE) + next_flush_idx = {({flush_idx.finish, flush_idx.set_num, flush_idx.frame_num} + 1'b1), N_BLOCK_BITS'('0)}; + end + + // decoded address conversion + assign decoded_addr = decoded_cache_addr_t'(proc_gen_bus_if.addr); + + // hit logic with pass through + always_comb begin + hit = 0; + hit_idx = 0; + hit_data = 0; + pass_through = proc_gen_bus_if.addr >= NONCACHE_START_ADDR; + + if (!pass_through) begin + for(int i = 0; i < ASSOC; i++) begin + if(sramRead.frames[i].tag == decoded_addr.tag_bits && sramRead.frames[i].valid) begin + hit = 1'b1; + hit_data = sramRead.frames[i].data; + hit_idx = i; + end + end + end + end + + // cache output logic + // Outputs: counter control signals, cache, signals to memory, signals to processor + always_comb begin + sramWEN = 0; + sramWrite = 0; + sramMask = '1; + proc_gen_bus_if.busy = 1; + proc_gen_bus_if.rdata = 0; // TODO: Can this be optimized? + mem_gen_bus_if.ren = 0; + mem_gen_bus_if.wen = 0; + mem_gen_bus_if.addr = 0; + mem_gen_bus_if.wdata = 0; + mem_gen_bus_if.byte_en = '1; // set this to all 1s for evictions + enable_flush_count = 0; + enable_word_count = 0; + enable_flush_count_nowb = 0; + clear_flush_count = 0; + clear_word_count = 0; + flush_done = 0; + idle_done = 0; + clear_done = 0; + next_read_addr = read_addr; + next_decoded_req_addr = decoded_req_addr; + next_last_used = last_used; + + // associativity, using NRU + if (ASSOC == 1 || (last_used[decoded_addr.idx_bits] == (ASSOC - 1))) + ridx = 0; + else + ridx = last_used[decoded_addr.idx_bits] + 1; + + // state dependent output logic + casez(state) + IDLE: begin + // clear out caches with flush + sramWEN = 1; + sramWrite.frames[flush_idx.frame_num] = '0; + sramMask.frames[flush_idx.frame_num] = '0; + enable_flush_count_nowb = 1; + // flag the completion of flush + if (flush_idx.finish) begin + clear_flush_count = 1; + idle_done = 1; + end + end + HIT: begin + next_read_addr = decoded_addr; + clear_word_count = 1; + // cache hit on a processor read + if(proc_gen_bus_if.ren && hit && !flush) begin + proc_gen_bus_if.busy = 0; + proc_gen_bus_if.rdata = hit_data[decoded_addr.block_bits]; + next_last_used[decoded_addr.idx_bits] = hit_idx; + end + // cache hit on a processor write + else if(proc_gen_bus_if.wen && hit && !flush) begin + proc_gen_bus_if.busy = 0; + sramWEN = 1; + casez (proc_gen_bus_if.byte_en) + 4'b0001: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'hFFFFFF00; + 4'b0010: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'hFFFF00FF; + 4'b0100: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'hFF00FFFF; + 4'b1000: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'h00FFFFFF; + 4'b0011: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'hFFFF0000; + 4'b1100: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'h0000FFFF; + default: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'h0; + endcase + sramMask.frames[hit_idx].dirty = 0; + sramWrite.frames[hit_idx].data[decoded_addr.block_bits] = proc_gen_bus_if.wdata; + sramWrite.frames[hit_idx].dirty = 1; + next_last_used[decoded_addr.idx_bits] = hit_idx; + end + // passthrough + else if(pass_through) begin + mem_gen_bus_if.wen = proc_gen_bus_if.wen; + mem_gen_bus_if.ren = proc_gen_bus_if.ren; + mem_gen_bus_if.addr = proc_gen_bus_if.addr; + mem_gen_bus_if.byte_en = proc_gen_bus_if.byte_en; + proc_gen_bus_if.busy = mem_gen_bus_if.busy; + proc_gen_bus_if.rdata = mem_gen_bus_if.rdata; + if(proc_gen_bus_if.wen) begin + casez (proc_gen_bus_if.byte_en) + 4'b0001: mem_gen_bus_if.wdata = {24'd0, proc_gen_bus_if.wdata[7:0]}; + 4'b0010: mem_gen_bus_if.wdata = {16'd0,proc_gen_bus_if.wdata[15:8],8'd0}; + 4'b0100: mem_gen_bus_if.wdata = {8'd0, proc_gen_bus_if.wdata[23:16], 16'd0}; + 4'b1000: mem_gen_bus_if.wdata = {proc_gen_bus_if.wdata[31:24], 24'd0}; + 4'b0011: mem_gen_bus_if.wdata = {16'd0, proc_gen_bus_if.wdata[15:0]}; + 4'b1100: mem_gen_bus_if.wdata = {proc_gen_bus_if.wdata[31:16],16'd0}; + default: mem_gen_bus_if.wdata = proc_gen_bus_if.wdata; + endcase + end + end + // cache miss on a clean block + else if((proc_gen_bus_if.ren || proc_gen_bus_if.wen) && ~hit && ~sramRead.frames[ridx].dirty && ~pass_through) begin + next_decoded_req_addr = decoded_addr; + next_read_addr = {decoded_addr.tag_bits, decoded_addr.idx_bits, N_BLOCK_BITS'('0), 2'b00}; + end + // cache miss on a dirty block + else if((proc_gen_bus_if.ren || proc_gen_bus_if.wen) && ~hit && sramRead.frames[ridx].dirty && ~pass_through) begin + next_decoded_req_addr = decoded_addr; + next_read_addr = {sramRead.frames[ridx].tag, decoded_addr.idx_bits, N_BLOCK_BITS'('0), 2'b00}; + end + end + FETCH: begin + // set cache to be invalid before cache completes fetch + mem_gen_bus_if.ren = 1; + mem_gen_bus_if.addr = read_addr; + sramMask.frames[ridx].valid = 0; + sramWrite.frames[ridx].valid = 0; + // fill data + if(~mem_gen_bus_if.busy) begin + sramWEN = 1'b1; + enable_word_count = 1'b1; + next_read_addr = read_addr + 4; + sramWrite.frames[ridx].data[word_num] = mem_gen_bus_if.rdata; + sramMask.frames[ridx].data[word_num] = 1'b0; + end + // complete fetch transaction from memory + if(word_count_done) begin + sramWEN = 1; + clear_word_count = 1'b1; + sramWrite.frames[ridx].valid = 1'b1; + sramWrite.frames[ridx].tag = decoded_req_addr.tag_bits; + sramMask.frames[ridx].valid = 1'b0; + sramMask.frames[ridx].tag = 1'b0; + end + end + WB: begin + // set stim for eviction + mem_gen_bus_if.wen = 1'b1; + mem_gen_bus_if.addr = read_addr; + mem_gen_bus_if.wdata = sramRead.frames[ridx].data[word_num]; + // increment eviction word counter + if(~mem_gen_bus_if.busy) begin + enable_word_count = 1; + next_read_addr = read_addr + 4; + end + // invalidate when eviction is complete + if(word_count_done) begin + sramWEN = 1; + clear_word_count = 1; + sramWrite.frames[ridx].dirty = 0; + sramMask.frames[ridx].dirty = 0; + sramWrite.frames[ridx].valid = 0; + sramMask.frames[ridx].valid = 0; + next_read_addr = {decoded_addr.tag_bits, decoded_addr.idx_bits, N_BLOCK_BITS'('0), 2'b00}; + end + end + FLUSH_CACHE: begin + // flush to memory if valid & dirty + if (sramRead.frames[flush_idx.frame_num].valid && sramRead.frames[flush_idx.frame_num].dirty) begin + mem_gen_bus_if.wen = 1'b1; + mem_gen_bus_if.addr = {sramRead.frames[flush_idx.frame_num].tag, flush_idx.set_num, flush_idx.word_num, 2'b00}; + mem_gen_bus_if.wdata = sramRead.frames[flush_idx.frame_num].data[flush_idx.word_num]; + // increment to next word when flush of word is done + if (~mem_gen_bus_if.busy) begin + enable_flush_count = 1; + // clears entry when flushed + if (flush_idx.word_num == (BLOCK_SIZE - 1)) begin + sramWEN = 1; + sramWrite.frames[flush_idx.frame_num] = 0; + sramMask.frames[flush_idx.frame_num] = 0; + end + end + end + // else clears entry, moves to next frame + else begin + sramWEN = 1; + sramWrite.frames[flush_idx.frame_num] = 0; + sramMask.frames[flush_idx.frame_num] = 0; + enable_flush_count_nowb = 1; + end + // flag the completion of flush + if (flush_idx.finish) begin + clear_flush_count = 1; + flush_done = 1; + end + end + endcase + end + + // next state logic + always_comb begin + next_state = state; + casez(state) + IDLE: begin + if (idle_done) + next_state = HIT; + end + HIT: begin + if ((proc_gen_bus_if.ren || proc_gen_bus_if.wen) && ~hit && sramRead.frames[ridx].dirty && ~pass_through) + next_state = WB; + else if ((proc_gen_bus_if.ren || proc_gen_bus_if.wen) && ~hit && ~sramRead.frames[ridx].dirty && ~pass_through) + next_state = FETCH; + if (flush || flush_req) + next_state = FLUSH_CACHE; + end + FETCH: begin + if (decoded_addr != decoded_req_addr) + next_state = HIT; + else if (word_count_done) + next_state = HIT; + end + WB: begin + if (decoded_addr != decoded_req_addr) + next_state = HIT; + else if (word_count_done) + next_state = FETCH; + end + FLUSH_CACHE: begin + if (flush_done) + next_state = HIT; + end + endcase + end + + // flush saver + always_comb begin + nflush_req = flush_req; + if (flush) + nflush_req = 1; + if (state == FLUSH_CACHE) + nflush_req = 0; + end + +endmodule diff --git a/source_code/caches/memory_arbiter.sv b/source_code/caches/memory_arbiter.sv new file mode 100644 index 000000000..e3cebb9ef --- /dev/null +++ b/source_code/caches/memory_arbiter.sv @@ -0,0 +1,100 @@ +`include "generic_bus_if.vh" + +module memory_arbiter ( + input CLK, nRST, + generic_bus_if.generic_bus icache_if, dcache_if, + generic_bus_if.cpu mem_arb_if +); + typedef enum logic[1:0] {IDLE, IREQUEST, DREQUEST} state_t; + state_t state, next_state; + + always_comb begin : OUTPUT_LOGIC + mem_arb_if.ren = '0; + mem_arb_if.wen = '0; + mem_arb_if.addr = '0; + mem_arb_if.wdata = '0; + mem_arb_if.byte_en = '0; + + icache_if.busy = '1; + icache_if.rdata = '0; + + dcache_if.busy = '1; + dcache_if.rdata = '0; + + case(state) + IDLE: begin + if(dcache_if.wen || dcache_if.ren) begin + mem_arb_if.ren = dcache_if.ren; + mem_arb_if.wen = dcache_if.wen; + mem_arb_if.addr = dcache_if.addr; + mem_arb_if.wdata = dcache_if.wdata; + mem_arb_if.byte_en = dcache_if.byte_en; + dcache_if.busy = mem_arb_if.busy; + dcache_if.rdata = mem_arb_if.rdata; + end + else if(icache_if.wen || icache_if.ren) begin + mem_arb_if.ren = icache_if.ren; + mem_arb_if.wen = icache_if.wen; + mem_arb_if.addr = icache_if.addr; + mem_arb_if.wdata = icache_if.wdata; + mem_arb_if.byte_en = icache_if.byte_en; + icache_if.busy = mem_arb_if.busy; + icache_if.rdata = mem_arb_if.rdata; + end + end + IREQUEST: begin + mem_arb_if.ren = icache_if.ren; + mem_arb_if.wen = icache_if.wen; + mem_arb_if.addr = icache_if.addr; + mem_arb_if.wdata = icache_if.wdata; + mem_arb_if.byte_en = icache_if.byte_en; + icache_if.busy = mem_arb_if.busy; + icache_if.rdata = mem_arb_if.rdata; + end + DREQUEST: begin + mem_arb_if.ren = dcache_if.ren; + mem_arb_if.wen = dcache_if.wen; + mem_arb_if.addr = dcache_if.addr; + mem_arb_if.wdata = dcache_if.wdata; + mem_arb_if.byte_en = dcache_if.byte_en; + dcache_if.busy = mem_arb_if.busy; + dcache_if.rdata = mem_arb_if.rdata; + end + endcase + end + + always_comb begin : NEXT_STATE_LOGIC + next_state = state; + + case(state) + IDLE: begin + if((dcache_if.wen || dcache_if.ren) && mem_arb_if.busy) begin + next_state = DREQUEST; + end + else if((icache_if.wen || icache_if.ren) && mem_arb_if.busy) begin + next_state = IREQUEST; + end + end + DREQUEST: begin + if(~mem_arb_if.busy) begin // hopefully, busy will always be high until fetch, so no problem + next_state = IDLE; + end + end + IREQUEST: begin + if(~mem_arb_if.busy) begin + next_state = IDLE; + end + end + endcase + end + + always_ff @ (posedge CLK, negedge nRST) begin + if(~nRST) begin + state <= IDLE; + end + else begin + state <= next_state; + end + end + +endmodule diff --git a/source_code/caches/separate_caches.sv b/source_code/caches/separate_caches.sv index 12d2c1eb9..92ee0e420 100644 --- a/source_code/caches/separate_caches.sv +++ b/source_code/caches/separate_caches.sv @@ -60,6 +60,23 @@ module separate_caches ( .flush_done(cc_if.dflush_done), .clear_done(cc_if.dclear_done) ); + "l1": + l1_cache #( + .CACHE_SIZE(DCACHE_SIZE), + .BLOCK_SIZE(DCACHE_BLOCK_SIZE), + .ASSOC(DCACHE_ASSOC), + .NONCACHE_START_ADDR(NONCACHE_START_ADDR) + ) + dcache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(dcache_mem_gen_bus_if), + .proc_gen_bus_if(dcache_proc_gen_bus_if), + .flush(cc_if.dcache_flush), + .clear(cc_if.dcache_clear), + .flush_done(cc_if.dflush_done), + .clear_done(cc_if.dclear_done) + ); endcase endgenerate @@ -88,6 +105,23 @@ module separate_caches ( .flush_done(cc_if.iflush_done), .clear_done(cc_if.iclear_done) ); + "l1": + l1_cache #( + .CACHE_SIZE(ICACHE_SIZE), + .BLOCK_SIZE(ICACHE_BLOCK_SIZE), + .ASSOC(ICACHE_ASSOC), + .NONCACHE_START_ADDR(NONCACHE_START_ADDR) + ) + icache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(icache_mem_gen_bus_if), + .proc_gen_bus_if(icache_proc_gen_bus_if), + .flush(cc_if.icache_flush), + .clear(cc_if.icache_clear), + .flush_done(cc_if.iflush_done), + .clear_done(cc_if.iclear_done) + ); endcase endgenerate diff --git a/source_code/caches/sram/run_sram_test.sh b/source_code/caches/sram/run_sram_test.sh new file mode 100755 index 000000000..33303f3d1 --- /dev/null +++ b/source_code/caches/sram/run_sram_test.sh @@ -0,0 +1,2 @@ +vlog -sv -work work *.sv +incdir+../../include +vsim -c -voptargs="+acc" work.sram_tb -do "do wave.do; run -all" diff --git a/source_code/caches/sram/sram.sv b/source_code/caches/sram/sram.sv new file mode 100644 index 000000000..feee4d519 --- /dev/null +++ b/source_code/caches/sram/sram.sv @@ -0,0 +1,54 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sram.sv +* +* Created by: Jimmy Mingze Jin +* Email: jin357@purdue.edu +* Date Created: 01/29/2023 +* Description: SRAM dummy module +* - w/r size in bits +*/ + +module sram #( + parameter SRAM_WR_SIZE = 128, + parameter SRAM_HEIGHT = 128 +) +( + input logic CLK, nRST, + input logic [SRAM_WR_SIZE-1:0] wVal, + output logic [SRAM_WR_SIZE-1:0] rVal, + input logic REN, WEN, + input logic [$clog2(SRAM_HEIGHT)-1:0] SEL, + input logic [SRAM_WR_SIZE-1:0] wMask +); + typedef logic [SRAM_WR_SIZE-1:0] sram_entry_size_t; + sram_entry_size_t [SRAM_HEIGHT-1:0] sramMemory; + sram_entry_size_t [SRAM_HEIGHT-1:0] n_sramMemory; + + always_ff @(posedge CLK) begin + sramMemory <= n_sramMemory; + end + + always_comb begin + n_sramMemory = sramMemory; + rVal = 32'hBAD0BAD0; + if (WEN) + n_sramMemory[SEL] = (wVal & ~wMask) | (wMask & sramMemory[SEL]); + if (REN) + rVal = sramMemory[SEL]; + end +endmodule \ No newline at end of file diff --git a/source_code/caches/sram/sram_tb.sv b/source_code/caches/sram/sram_tb.sv new file mode 100644 index 000000000..891b5a9ff --- /dev/null +++ b/source_code/caches/sram/sram_tb.sv @@ -0,0 +1,96 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sram_tb.sv +* +* Created by: Jimmy Mingze Jin +* Email: jin357@purdue.edu +* Date Created: 01/29/2023 +* Description: basic sram tb +*/ + +// setup +localparam CLK_PERIOD = 10; +localparam TB_SRAM_WR_SIZE = 128; +localparam TB_SRAM_HEIGHT = 128; +localparam TB_IS_BIDIRECTIONAL = 0; +typedef logic [TB_SRAM_WR_SIZE-1:0] sram2_entry_size_t; // is this legal + +`timescale 1ns/10ps +module sram_tb(); + // CLK/nRST + logic CLK = 0, nRST = 1; + always #(CLK_PERIOD/2) CLK++; + + + + // tb vars + sram2_entry_size_t wVal, rVal; + logic REN, WEN; + logic [$clog2(TB_SRAM_HEIGHT):0] SEL; + + // DUT instance. + sram #(.SRAM_WR_SIZE(TB_SRAM_WR_SIZE), + .SRAM_HEIGHT(TB_SRAM_HEIGHT), + .IS_BIDIRECTIONAL(TB_IS_BIDIRECTIONAL)) + DUT (CLK, nRST, wVal, rVal, REN, WEN, SEL); + + task reset_dut; + @(negedge CLK) nRST = 1'b0; + #(CLK_PERIOD * 2) nRST = 1'b1; + @(posedge CLK); + endtask + + initial begin + // set input signals. + REN = 0; + WEN = 0; + SEL = 0; + wVal = 0; + $timeformat(-9, 0, " ns", 20); + reset_dut(); + #(CLK_PERIOD); + + // try to fill data + for (SEL = 0; SEL < TB_SRAM_HEIGHT; SEL++) begin + wVal = 1 + SEL; + WEN = 1; + #(CLK_PERIOD * 2); + WEN = 0; + #(CLK_PERIOD); + end + + #(CLK_PERIOD); + + // try to read in order + for (SEL = 0; SEL < TB_SRAM_HEIGHT; SEL++) begin + REN = 1; + #(CLK_PERIOD * 1.5); + if (rVal != SEL + 1) + $display("expected %d but got %d at time %4t", SEL + 1, rVal, $time); + else + $display("correctly got %d at time %4t", rVal, $time); + + #(CLK_PERIOD * 0.5); + REN = 0; + #(CLK_PERIOD); + end + + #(CLK_PERIOD); + + $finish; + end +endmodule \ No newline at end of file diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index 0084dd15d..b1921c53a 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -230,7 +230,7 @@ module stage3_execute_stage ( assign fw_if.rs2_e = rf_if.rs2; assign hazard_if.pc_e = fetch_ex_if.fetch_ex_reg.pc; - assign hazard_if.ex_busy = rv32m_busy; // Add & conditions here for other FUs that can stall + assign hazard_if.ex_busy = (rv32m_busy && cu_if.rv32m_control.select); // Add & conditions here for other FUs that can stall assign hazard_if.valid_e = fetch_ex_if.fetch_ex_reg.valid; diff --git a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv index 47c98cd60..54494ad14 100644 --- a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv @@ -55,7 +55,7 @@ module stage3_fetch_stage ( always_ff @(posedge CLK, negedge nRST) begin if (~nRST) begin pc <= RESET_PC; - end else if (hazard_if.pc_en | rv32cif.done_earlier) begin + end else if (hazard_if.pc_en /*| rv32cif.done_earlier*/) begin pc <= npc; end end diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv index 2d73810b8..f7d41a846 100644 --- a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -169,8 +169,9 @@ module stage3_hazard_unit ( // || (wait_for_imem && !dmem_access) // ??? //& (~ex_flush_hazard | e_ex_stage) // ??? //|| rm_if.execute_stall // - || hazard_if.ex_busy // Waiting for extension - || mem_use_stall; // Data hazard -- stall until dependency clears (from E/M flush after writeback) + || (hazard_if.ex_busy && !ex_flush_hazard && !branch_jump) // Ugly case -- need to flush for control hazards when X busy, but other cases require stalling to take priority to prevent data loss (e.g. slow instruction fetch, valid insn in X, load in M --> giving flush priority would destroy insn in X) + || mem_use_stall + || hazard_if.fence_stall; // Data hazard -- stall until dependency clears (from E/M flush after writeback) // TODO: Exceptions assign hazard_if.ex_mem_stall = wait_for_dmem // Second clause ensures we finish memory op on interrupt condition || hazard_if.fence_stall diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index a3dc6bd8a..2e9ab1016 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -132,6 +132,7 @@ module stage3_mem_stage( logic ifence_pulse; logic iflushed, iflushed_next; logic dflushed, dflushed_next; + logic iflush_done_reg, dflush_done_reg; always_ff @(posedge CLK, negedge nRST) begin if(!nRST) begin @@ -146,8 +147,21 @@ module stage3_mem_stage( end assign ifence_pulse = ex_mem_if.ex_mem_reg.ifence & ~ifence_reg; - assign iflushed_next = ifence_pulse ? 1'b0 : cc_if.iflush_done; - assign dflushed_next = ifence_pulse ? 1'b0 : cc_if.dflush_done; + assign cc_if.icache_flush = ifence_pulse; + assign cc_if.dcache_flush = ifence_pulse; + // holds iflushed/dflushed high when done, resets to 0 on a pulse + always_comb begin + iflushed_next = iflushed; + dflushed_next = dflushed; + if (ifence_pulse) begin + iflushed_next = 0; + dflushed_next = 0; + end + if (cc_if.iflush_done) + iflushed_next = 1; + if (cc_if.dflush_done) + dflushed_next = 1; + end /************************ * Hazard/Forwarding Unit @@ -155,7 +169,7 @@ module stage3_mem_stage( // Note: Some hazard unit signals are assigned below in the CSR section assign hazard_if.d_mem_busy = dgen_bus_if.busy; assign hazard_if.ifence = ex_mem_if.ex_mem_reg.ifence; - assign hazard_if.fence_stall = ex_mem_if.ex_mem_reg.ifence && (!dflushed || !iflushed); + assign hazard_if.fence_stall = ifence_reg && !(iflushed && dflushed); assign hazard_if.dren = ex_mem_if.ex_mem_reg.dren; assign hazard_if.dwen = ex_mem_if.ex_mem_reg.dwen; assign hazard_if.jump = ex_mem_if.ex_mem_reg.jump; diff --git a/source_code/trackers/cpu_tracker.sv b/source_code/trackers/cpu_tracker.sv index 1115fca12..4659ef21c 100644 --- a/source_code/trackers/cpu_tracker.sv +++ b/source_code/trackers/cpu_tracker.sv @@ -39,6 +39,7 @@ module cpu_tracker ( ); import rv32i_types_pkg::*; import machine_mode_types_1_12_pkg::*; + import rv32m_pkg::*; parameter int CPUID = 0; @@ -132,23 +133,38 @@ module cpu_tracker ( endcase end REGREG: begin - case (regreg_t'(funct3)) - ADDSUB: begin - if (instr_30) instr_mnemonic = "sub"; - else instr_mnemonic = "add"; - end - SLL: instr_mnemonic = "sll"; - SLT: instr_mnemonic = "slt"; - SLTU: instr_mnemonic = "sltu"; - XOR: instr_mnemonic = "xor"; - SR: begin - if (instr_30) instr_mnemonic = "sra"; - else instr_mnemonic = "srl"; - end - OR: instr_mnemonic = "or"; - AND: instr_mnemonic = "and"; - default: instr_mnemonic = "unknown regreg op"; - endcase + if(instr[31 -: 7] == RV32M_OPCODE_MINOR) begin + case (rv32m_op_t'(funct3)) + MUL: instr_mnemonic = "mul"; + MULH: instr_mnemonic = "mulh"; + MULHSU: instr_mnemonic = "mulhsu"; + MULHU: instr_mnemonic = "mulhu"; + DIV: instr_mnemonic = "div"; + DIVU: instr_mnemonic = "divu"; + REM: instr_mnemonic = "rem"; + REMU: instr_mnemonic = "remu"; + // No default -- full case + endcase + end else begin + case (regreg_t'(funct3)) + ADDSUB: begin + if (instr_30) instr_mnemonic = "sub"; + else instr_mnemonic = "add"; + end + SLL: instr_mnemonic = "sll"; + SLT: instr_mnemonic = "slt"; + SLTU: instr_mnemonic = "sltu"; + XOR: instr_mnemonic = "xor"; + SR: begin + if (instr_30) instr_mnemonic = "sra"; + else instr_mnemonic = "srl"; + end + OR: instr_mnemonic = "or"; + AND: instr_mnemonic = "and"; + + default: instr_mnemonic = "unknown regreg op"; + endcase + end end SYSTEM: begin case (rv32i_system_t'(funct3)) diff --git a/verification/u-mode/csr.c b/verification/u-mode/csr.c index 341df724c..fedb1e3f0 100644 --- a/verification/u-mode/csr.c +++ b/verification/u-mode/csr.c @@ -78,7 +78,8 @@ int main(void) { asm volatile("nop; nop; nop;"); asm volatile("csrr %0, cycle" : "=r"(csr_val_1)); - if (csr_val_1 - csr_val_0 < 15) // Are the two cycle counts close? + // TODO: This test is highly dependent on I-fetch speed + if (csr_val_1 - csr_val_0 < 30) // Are the two cycle counts close? { flag -= 1; } @@ -93,4 +94,4 @@ int main(void) { __builtin_unreachable(); -} \ No newline at end of file +} diff --git a/verification/uvm/caches/bfm/memory_bfm.svh b/verification/uvm/caches/bfm/memory_bfm.svh new file mode 100644 index 000000000..ede69779a --- /dev/null +++ b/verification/uvm/caches/bfm/memory_bfm.svh @@ -0,0 +1,174 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: memory_bfm.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Bus Functional Model (BFM) for the memory bus, including memory mapped I/O +*/ + +`ifndef MEMORY_BFM_SVH +`define MEMORY_BFM_SVH + +`include "cache_env_config.svh" + +`include "uvm_macros.svh" + +`include "dut_params.svh" + +`include "Utils.svh" + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +class memory_bfm extends uvm_component; + `uvm_component_utils(memory_bfm) + + virtual cache_if cif; + virtual generic_bus_if bus_if; + + cache_env_config env_config; + + word_t mem[word_t]; // initialized memory array + word_t mmio[word_t]; // initialized memory mapped array + + function new(string name = "memory_bfm", uvm_component parent); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + string bus_if_str; + super.build_phase(phase); + + // get config from database + if (!uvm_config_db#(cache_env_config)::get(this, "", "env_config", env_config)) begin + `uvm_fatal(this.get_name(), "env config not registered to db") + end + + // get interface from database + if (!uvm_config_db#(virtual cache_if)::get(this, "", "d_cif", cif)) begin + `uvm_fatal($sformatf("%s/d_cif", this.get_name()), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), "pulled from db", UVM_FULL) + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", "mem_bus_if", bus_if)) begin + `uvm_fatal($sformatf("%s/mem_bus_if", this.get_name()), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), "pulled from db", UVM_FULL) + endfunction : build_phase + + virtual task run_phase(uvm_phase phase); + forever begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + + // default values on bus + bus_if.busy = '0; + bus_if.rdata = 32'hbad0_bad0; + + if (bus_if.addr < `NONCACHE_START_ADDR) begin + if (bus_if.ren) begin + mem_read(); + end else if (bus_if.wen) begin + mem_write(); + end + end else if (bus_if.addr >= `NONCACHE_START_ADDR) begin + if (bus_if.ren) begin + mmio_read(); + end else if (bus_if.wen) begin + mmio_write(); + end + end + end + endtask : run_phase + + task mem_read(); + int count; + + bus_if.busy = '1; + count = 1; + while (count < env_config.mem_latency && bus_if.ren) begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + count++; + end + + bus_if.rdata = read(bus_if.addr); + bus_if.busy = '0; + endtask : mem_read + + task mem_write(); + int count; + + bus_if.busy = '1; + count = 1; + while (count < env_config.mem_latency) begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + count++; + end + + mem[bus_if.addr] = bus_if.wdata; + + bus_if.busy = '0; + endtask : mem_write + + task mmio_read(); + int count; + bus_if.busy = '1; + //TODO: COME UP WITH SOME MEANINGFUL DATA TO PUT FOR MMIO, MAYBE SIMULATE A PERIFERAL REGISTER + + count = 1; + while (count < env_config.mmio_latency && bus_if.ren) begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + count++; + end + + bus_if.rdata = {env_config.mmio_tag, bus_if.addr[15:0]}; + bus_if.busy = '0; + endtask : mmio_read + + task mmio_write(); + int count; + bus_if.busy = '1; + + count = 1; + while (count < env_config.mmio_latency && bus_if.wen) begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + count++; + end + + // mmio[bus_if.addr] = bus_if.wdata; //TODO: DO SOMETHING MORE MEANINGFUL FOR WRITING TO MMIO, REGISTER MODEL? + bus_if.busy = '0; + endtask : mmio_write + + function word_t read(word_t addr); + if (mem.exists(bus_if.addr)) begin + return mem[bus_if.addr]; + end else begin + return {env_config.mem_tag, addr[15:0]}; // return non-initialized data + end + endfunction : read + +endclass : memory_bfm + +`endif diff --git a/verification/uvm/caches/bus_agents/l2_agent.svh b/verification/uvm/caches/bus_agents/l2_agent.svh new file mode 100644 index 000000000..f2d04f150 --- /dev/null +++ b/verification/uvm/caches/bus_agents/l2_agent.svh @@ -0,0 +1,52 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: l2_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: UVM Agent to monitor the processor/l1 side of l2 cache +*/ + +`ifndef L2_AGENT_SHV +`define L2_AGENT_SHV + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "bus_monitor.svh" + +// typedef bus_monitor#(0, "l2_cif", "arb_l2_bus_if") l2_monitor; + +class l2_agent extends bus_agent; + `uvm_component_utils(l2_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + mon = bus_monitor::type_id::create("L2_MON", this); + mon.set_precedence(0); + mon.set_cif_str("l2_cif"); + mon.set_bus_if_str("arb_l2_bus_if"); + + `uvm_info(this.get_name(), $sformatf("Created <%s>", mon.get_name()), UVM_FULL) + endfunction + +endclass : l2_agent + +`endif diff --git a/verification/uvm/caches/bus_agents/mem_agent.svh b/verification/uvm/caches/bus_agents/mem_agent.svh new file mode 100644 index 000000000..7eaae21e9 --- /dev/null +++ b/verification/uvm/caches/bus_agents/mem_agent.svh @@ -0,0 +1,51 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mem_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Agent to stand in for the memory side of the caches +*/ + +`ifndef MEM_AGENT_SHV +`define MEM_AGENT_SHV + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "bus_monitor.svh" + +// typedef bus_monitor#(0, "l2_cif", "mem_bus_if") mem_monitor; + +class mem_agent extends bus_agent; + `uvm_component_utils(mem_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + mon = bus_monitor::type_id::create("MEM_MON", this); + mon.set_precedence(0); + mon.set_cif_str("l2_cif"); + mon.set_bus_if_str("mem_bus_if"); + `uvm_info(this.get_name(), $sformatf("Created <%s>", mon.get_name()), UVM_FULL) + endfunction + +endclass : mem_agent + +`endif diff --git a/verification/uvm/caches/bus_agents/mem_arb_agent.svh b/verification/uvm/caches/bus_agents/mem_arb_agent.svh new file mode 100644 index 000000000..a2ac4e2e6 --- /dev/null +++ b/verification/uvm/caches/bus_agents/mem_arb_agent.svh @@ -0,0 +1,72 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mem_arb_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/12/2022 +* Description: UVM Agent to stand in for the memory arbiter side of the l2 cache +*/ + +`ifndef MEM_ARB_AGENT_SVH +`define MEM_ARB_AGENT_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "cpu_driver.svh" +`include "bus_monitor.svh" +`include "bus_agent.svh" +`include "cpu_sequencer.svh" + +class mem_arb_driver extends cpu_driver #("l2_cif", "arb_l2_bus_if"); + `uvm_component_utils(mem_arb_driver) + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction +endclass + +class mem_arb_agent extends bus_agent #(mem_arb_driver); + `uvm_component_utils(mem_arb_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + sqr = cpu_sequencer::type_id::create("MEM_ARB_SQR", this); + drv = mem_arb_driver::type_id::create("MEM_ARB_DRV", this); + mon = bus_monitor::type_id::create("MEM_ARB_MON", this); + mon.set_precedence(1); + mon.set_cif_str("l2_cif"); + mon.set_bus_if_str("arb_l2_bus_if"); + + `uvm_info(this.get_name(), $sformatf( + "Created <%s>, <%s>, <%s>", drv.get_name(), sqr.get_name(), mon.get_name()), UVM_FULL) + endfunction + + virtual function void connect_phase(uvm_phase phase); + drv.seq_item_port.connect(sqr.seq_item_export); + `uvm_info(this.get_name(), $sformatf("Connected <%s> to <%s>", drv.get_name(), sqr.get_name()), + UVM_FULL) + endfunction + +endclass : mem_arb_agent +`endif diff --git a/verification/uvm/caches/coverage/.gitignore b/verification/uvm/caches/coverage/.gitignore new file mode 100644 index 000000000..bd14f4040 --- /dev/null +++ b/verification/uvm/caches/coverage/.gitignore @@ -0,0 +1,4 @@ +# Note: this folder needs to exist to hold the coverage files + +# However; we ignore all its contents +*.ucdb \ No newline at end of file diff --git a/verification/uvm/caches/cpu_agent/cpu_driver.svh b/verification/uvm/caches/cpu_agent/cpu_driver.svh new file mode 100644 index 000000000..121787b2e --- /dev/null +++ b/verification/uvm/caches/cpu_agent/cpu_driver.svh @@ -0,0 +1,124 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cpu_driver.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Driver class for initiating processor side requests to caches +*/ + +`ifndef CPU_DRIVER_SVH +`define CPU_DRIVER_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "generic_bus_if.vh" +`include "cache_if.svh" + +class cpu_driver #( + string cif_str, + string bus_if_str +) extends uvm_driver #(cpu_transaction); + `uvm_component_param_utils(cpu_driver#(cif_str, bus_if_str)) + + virtual cache_if cif; + virtual generic_bus_if cpu_bus_if; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction : new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + // get interface from database + if (!uvm_config_db#(virtual cache_if)::get(this, "", cif_str, cif)) begin + `uvm_fatal($sformatf("%s/%s", this.get_name(), cif_str), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), $sformatf("pulled <%s> from db", cif_str), UVM_FULL) + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", bus_if_str, cpu_bus_if)) begin + `uvm_fatal($sformatf("%s/%s", this.get_name(), bus_if_str), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), $sformatf("pulled <%s> from db", bus_if_str), UVM_FULL) + endfunction : build_phase + + task run_phase(uvm_phase phase); + cpu_transaction req_item; + + DUT_reset(); + + forever begin + seq_item_port.get_next_item(req_item); + `uvm_info(this.get_name(), $sformatf("Received new sequence item:\n%s", req_item.sprint()), + UVM_HIGH) + + cpu_bus_if.addr = req_item.addr; + cpu_bus_if.wdata = req_item.data; + cpu_bus_if.ren = ~req_item.rw; // read = 0 + cpu_bus_if.wen = req_item.rw; // write = 1 + + cpu_bus_if.byte_en = req_item.byte_en; + cif.flush = req_item.flush; + + //FIXME: NEED TO ADD CLEAR FUNCTIONALITY + cif.clear = '0; + + if (cif.flush) begin + do begin + @(posedge cif.CLK); //wait for flush to complete + end while (cif.flush_done == 1'b0); + end else begin + do begin + @(posedge cif.CLK); //wait for cache operation to complete + end while (cpu_bus_if.busy == 1'b1); + end + + seq_item_port.item_done(); + end + endtask : run_phase + + task DUT_reset(); + // reset all cpu bus signals + cpu_bus_if.addr = '0; + cpu_bus_if.wdata = '0; + cpu_bus_if.ren = '0; + cpu_bus_if.wen = '0; + cpu_bus_if.byte_en = '0; + + // reset all cpu cif request signals + cif.nRST = 0; + cif.clear = 0; + cif.flush = 0; + + @(posedge cif.CLK); + cif.nRST = 1; + @(posedge cif.CLK); + cif.nRST = 0; + @(posedge cif.CLK); + cif.nRST = 1; + @(posedge cif.CLK); + endtask + +endclass : cpu_driver + +`endif diff --git a/verification/uvm/caches/cpu_agent/cpu_sequencer.svh b/verification/uvm/caches/cpu_agent/cpu_sequencer.svh new file mode 100644 index 000000000..0acca680a --- /dev/null +++ b/verification/uvm/caches/cpu_agent/cpu_sequencer.svh @@ -0,0 +1,32 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cpu_sequencer.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: UVM Sequencer class for initiating processor side requests to caches +*/ + +`ifndef CPU_SEQUENCER_SVH +`define CPU_SEQUENCER_SVH + +`include "cpu_transaction.svh" + +typedef uvm_sequencer#(cpu_transaction) cpu_sequencer; + +`endif diff --git a/verification/uvm/caches/cpu_agent/cpu_transaction.svh b/verification/uvm/caches/cpu_agent/cpu_transaction.svh new file mode 100644 index 000000000..966b5f642 --- /dev/null +++ b/verification/uvm/caches/cpu_agent/cpu_transaction.svh @@ -0,0 +1,69 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cpu_transaction.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Abstracted memory request transaction +*/ + +`ifndef CPU_TRANSACTION_SVH +`define CPU_TRANSACTION_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; +`include "uvm_macros.svh" +`include "cache_env.svh" +`include "dut_params.svh" + +class cpu_transaction extends uvm_sequence_item; + + rand bit rw; // 0 -> read; 1 -> write + rand word_t addr; + rand word_t data; + + rand logic [3:0] byte_en; + + rand bit flush; + //TODO: ADD CLEAR + + `uvm_object_utils_begin(cpu_transaction) + `uvm_field_int(rw, UVM_ALL_ON) + `uvm_field_int(addr, UVM_ALL_ON) + `uvm_field_int(data, UVM_ALL_ON) + `uvm_field_int(byte_en, UVM_ALL_ON) + `uvm_field_int(flush, UVM_ALL_ON) + `uvm_object_utils_end + + constraint valid_addr { + addr >= '0; + soft addr < `NONCACHE_START_ADDR; + addr[1:0] == '0; + } + + constraint usable_byte_en { + byte_en inside {4'b0001, 4'b0010, 4'b0100, 4'b1000, 4'b0011, 4'b1100, 4'b1111}; + } + + function new(string name = "cpu_transaction"); + super.new(name); + endfunction : new + +endclass : cpu_transaction + +`endif diff --git a/verification/uvm/caches/cpu_agent/d_cpu_agent.svh b/verification/uvm/caches/cpu_agent/d_cpu_agent.svh new file mode 100644 index 000000000..65af0ca92 --- /dev/null +++ b/verification/uvm/caches/cpu_agent/d_cpu_agent.svh @@ -0,0 +1,73 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: d_cpu_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: UVM Agent to stand in for the processor side of the data cache +*/ + +`ifndef D_CPU_AGENT_SVH +`define D_CPU_AGENT_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "cpu_driver.svh" +`include "bus_monitor.svh" +`include "bus_agent.svh" +`include "cpu_sequencer.svh" + +class d_cpu_driver extends cpu_driver #("d_cif", "d_cpu_bus_if"); + `uvm_component_utils(d_cpu_driver) + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction +endclass + +class d_cpu_agent extends bus_agent #(d_cpu_driver); + `uvm_component_utils(d_cpu_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + sqr = cpu_sequencer::type_id::create("D_CPU_SQR", this); + drv = d_cpu_driver::type_id::create("D_CPU_DRV", this); + mon = bus_monitor::type_id::create("D_CPU_MON", this); + mon.set_precedence(1); + mon.set_cif_str("d_cif"); + mon.set_bus_if_str("d_cpu_bus_if"); + + `uvm_info(this.get_name(), $sformatf( + "Created <%s>, <%s>, <%s>", drv.get_name(), sqr.get_name(), mon.get_name()), UVM_FULL) + endfunction + + virtual function void connect_phase(uvm_phase phase); + drv.seq_item_port.connect(sqr.seq_item_export); + `uvm_info(this.get_name(), $sformatf("Connected <%s> to <%s>", drv.get_name(), sqr.get_name()), + UVM_FULL) + endfunction + +endclass : d_cpu_agent + +`endif diff --git a/verification/uvm/caches/cpu_agent/i_cpu_agent.svh b/verification/uvm/caches/cpu_agent/i_cpu_agent.svh new file mode 100644 index 000000000..0a73e5f50 --- /dev/null +++ b/verification/uvm/caches/cpu_agent/i_cpu_agent.svh @@ -0,0 +1,73 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: i_cpu_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: UVM Agent to stand in for the processor side of the instruction cache +*/ + +`ifndef I_CPU_AGENT_SVH +`define I_CPU_AGENT_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "cpu_driver.svh" +`include "bus_monitor.svh" +`include "bus_agent.svh" +`include "cpu_sequencer.svh" + +class i_cpu_driver extends cpu_driver #("i_cif", "i_cpu_bus_if"); + `uvm_component_utils(i_cpu_driver) + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction +endclass + +class i_cpu_agent extends bus_agent #(i_cpu_driver); + `uvm_component_utils(i_cpu_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + sqr = cpu_sequencer::type_id::create("I_CPU_SQR", this); + drv = i_cpu_driver::type_id::create("I_CPU_DRV", this); + mon = bus_monitor::type_id::create("I_CPU_MON", this); + mon.set_precedence(1); + mon.set_cif_str("i_cif"); + mon.set_bus_if_str("i_cpu_bus_if"); + + `uvm_info(this.get_name(), $sformatf( + "Created <%s>, <%s>, <%s>", drv.get_name(), sqr.get_name(), mon.get_name()), UVM_FULL) + endfunction + + virtual function void connect_phase(uvm_phase phase); + drv.seq_item_port.connect(sqr.seq_item_export); + `uvm_info(this.get_name(), $sformatf("Connected <%s> to <%s>", drv.get_name(), sqr.get_name()), + UVM_FULL) + endfunction + +endclass : i_cpu_agent + +`endif diff --git a/verification/uvm/caches/end2end/end2end.svh b/verification/uvm/caches/end2end/end2end.svh new file mode 100644 index 000000000..891a45bd6 --- /dev/null +++ b/verification/uvm/caches/end2end/end2end.svh @@ -0,0 +1,233 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: end2end.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Component for ensuring proper translation between processor and memory side of the caches +*/ + +`ifndef END2END_SVH +`define END2END_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "dut_params.svh" +`include "cpu_transaction.svh" +`include "cache_model.svh" +`include "Utils.svh" + +`uvm_analysis_imp_decl(_src_req) +`uvm_analysis_imp_decl(_src_resp) +`uvm_analysis_imp_decl(_dest_resp) + +class end2end extends uvm_component; + `uvm_component_utils(end2end) + + uvm_analysis_imp_src_req #(cpu_transaction, end2end) src_req_export; // src's request + uvm_analysis_imp_src_resp #(cpu_transaction, end2end) src_resp_export; // dest's response to src's request + uvm_analysis_imp_dest_resp #(cpu_transaction, end2end) dest_resp_export; // + + cache_model cache; // holds values currently stored in cache + + cpu_transaction history[$]; // holds recent mem bus transactions + + int successes, errors; // records number of matches and mismatches + + function new(string name, uvm_component parent = null); + super.new(name, parent); + src_req_export = new("src_req_ap", this); + src_resp_export = new("src_resp_ap", this); + dest_resp_export = new("dest_resp_ap", this); + endfunction : new + + function void write_src_req(cpu_transaction t); + cpu_transaction tx = cpu_transaction::type_id::create("src_req_tx", this); + tx.copy(t); + + `uvm_info(this.get_name(), $sformatf("Detected CPU Request @%h", tx.addr), UVM_MEDIUM); + + if (history.size() > 0) begin + flush_history(); + end + endfunction : write_src_req + + function void write_src_resp(cpu_transaction t); + cpu_transaction tx = cpu_transaction::type_id::create("src_resp_tx", this); + tx.copy(t); + + `uvm_info(this.get_name(), $sformatf("Detected CPU Response @%h", tx.addr), UVM_MEDIUM); + `uvm_info(this.get_name(), $sformatf("cache before:\n%s", cache.sprint()), UVM_HIGH) + + if (tx.flush) begin : FLUSH + // flush request + + flush_history(); // takes care of any dirty data WBs from flush + + if (cache.dirty()) begin + errors++; + `uvm_error(this.get_name(), "Error: Cache Flush -> Cache Contains Dirty Data"); + `uvm_info(this.get_name(), $sformatf("%s", cache.sprint()), UVM_LOW); + end else begin + successes++; + `uvm_info(this.get_name(), "Success: Cache Flush -> No Dirty Data", UVM_LOW); + end + + cache.flush(); // flush all entries from cache model + end : FLUSH + else if (tx.addr < `NONCACHE_START_ADDR) begin : CACHEABLE + // memory request + if (history.size() == 0) begin : QUIET_MEM_BUS + // quiet memory bus + + if (cache.exists(tx.addr)) begin + // data is cached + successes++; + `uvm_info(this.get_name(), "Success: Cache Hit -> Quiet Mem Bus", UVM_LOW); + end else begin + // data not in cache + errors++; + `uvm_error(this.get_name(), "Error: Cache Miss -> Quiet Mem Bus"); + end + end : QUIET_MEM_BUS + else begin : ACTIVE_MEM_BUS + // active memory bus + if (cache.exists(tx.addr)) begin + // data is already cached + errors++; + `uvm_error(this.get_name(), "Error: Cache Hit -> Active Mem Bus"); + end else begin + // data not in cache, need to get data from memory + flush_history(); + + if (cache.exists(tx.addr)) begin + successes++; + `uvm_info(this.get_name(), "Success: Cache Miss -> Active Mem Bus", UVM_LOW); + end else begin + errors++; + `uvm_error(this.get_name(), + "Error: Data Requested by CPU is not pressent in cache after mem bus txns"); + end + end + end : ACTIVE_MEM_BUS + + if (tx.rw) begin + // update cache on PrWr + cache.update(tx.addr, tx.data, tx.byte_en); + end + end : CACHEABLE + else begin : MEM_MAPPED + // memory mapped io request + + if (history.size() == 1) begin + cpu_transaction mapped = history.pop_front(); + //FIXME:CHECK THAT THIS IS PROPER WAY TO DEAL WITH THIS + if (!cache.ignore_mask & tx.rw) begin + `uvm_info(this.get_name(), "Using Byte Mask for Memory Mapped Data", UVM_LOW); + tx.data = Utils::byte_mask(tx.byte_en) & tx.data; + end + if (mapped.compare(tx)) begin + successes++; + `uvm_info(this.get_name(), "Success: Mem Mapped I/O Pass Through Match", UVM_LOW); + end else begin + errors++; + `uvm_error(this.get_name(), "Error: Mem Mapped I/O Pass Through Mismatch"); + `uvm_info(this.get_name(), $sformatf( + "\ncpu req:\n%s\nmem bus:\n%s", tx.sprint(), mapped.sprint()), UVM_LOW) + end + end else begin + errors++; + `uvm_error( + this.get_name(), + $sformatf( + "Error: Mem Mapped I/O Pass Through Transaction Size Mismatch: expected 1, actual %0d", + history.size())); + end + end : MEM_MAPPED + endfunction : write_src_resp + + function void write_dest_resp(cpu_transaction t); + cpu_transaction tx = cpu_transaction::type_id::create("dest_resp_tx", this); + tx.copy(t); + + `uvm_info(this.get_name(), $sformatf("Detected Memory Response:: addr=%h", tx.addr), + UVM_MEDIUM); + + history.push_back(tx); + endfunction : write_dest_resp + + function void report_phase(uvm_phase phase); + `uvm_info(this.get_name(), $sformatf("Successes: %0d", successes), UVM_LOW); + `uvm_info(this.get_name(), $sformatf("Errors: %0d", errors), UVM_LOW); + endfunction + + function void handle_mem_tx(cpu_transaction mem_tx); + if (mem_tx.rw) begin + // write + // writes are cache evictions + if (cache.remove(mem_tx.addr, mem_tx.data)) begin + successes++; + `uvm_info(this.get_name(), $sformatf("Word sucessfully removed from cache model: 0x%h", + mem_tx.addr), UVM_LOW); + end else begin + errors++; + `uvm_error(this.get_name(), $sformatf("Error when removing word from cache model: 0x%h", + mem_tx.addr)); + end + end else begin + // read + cache.insert(mem_tx.addr, mem_tx.data, mem_tx.byte_en); + end + endfunction : handle_mem_tx + + function void flush_history(); + int block_idx = 0; + + if (history.size() % `L1_BLOCK_SIZE != 0) begin + errors++; + `uvm_error( + this.get_name(), + $sformatf( + "memory word requests do not match block size: requested %0d, not evenly divisible by: %0d", + history.size(), `L1_BLOCK_SIZE)); + end + + while (history.size() > 0) begin + cpu_transaction t = history.pop_front(); + handle_mem_tx(t); + block_idx++; + if (block_idx % `L1_BLOCK_SIZE == 0) begin + // last word of block + if (cache.is_valid_block(t.addr)) begin + successes++; + `uvm_info(this.get_name(), $sformatf("Valid block txn with memory: %h", t.addr), UVM_LOW); + end else begin + errors++; + `uvm_error(this.get_name(), + $sformatf("Invalid word addresses for block txn with memory: %h", t.addr)); + `uvm_info(this.get_name(), $sformatf("%s", cache.sprint()), UVM_LOW); + end + end + end + endfunction : flush_history + +endclass : end2end + +`endif diff --git a/verification/uvm/caches/env/cache_env.svh b/verification/uvm/caches/env/cache_env.svh new file mode 100644 index 000000000..b2cd29afe --- /dev/null +++ b/verification/uvm/caches/env/cache_env.svh @@ -0,0 +1,237 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cache_env.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Environment for cache verification +*/ + +`ifndef CACHE_ENV_SVH +`define CACHE_ENV_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" + +`include "cache_env_config.svh" // config +`include "memory_bfm.svh" // bfm + +`include "bus_predictor.svh" // uvm_subscriber +`include "bus_scoreboard.svh" // uvm_scoreboard + +`include "d_cpu_agent.svh" +`include "i_cpu_agent.svh" +`include "mem_arb_agent.svh" + +`include "cpu_transaction.svh" // uvm_sequence_item + +`include "mem_agent.svh" +`include "l2_agent.svh" +`include "mem_arb_agent.svh" + +`include "end2end.svh" // uvm_scoreboard + +typedef uvm_analysis_port#(cpu_transaction) cpu_ap; + +class cache_env extends uvm_env; + `uvm_component_utils(cache_env) + + function new(string name = "env", uvm_component parent = null); + super.new(name, parent); + endfunction + + cache_env_config env_config; //environment configuration + memory_bfm mem_bfm; //memory bus functional model + +`ifdef TB_L1_CONFIG + d_cpu_agent cpu_agt; // contains monitor and driver + bus_predictor cpu_pred; // a reference model to check the result + bus_scoreboard cpu_score; // scoreboard + + mem_agent mem_agt; // contains monitor + bus_predictor mem_pred; // a reference model to check the result + bus_scoreboard mem_score; // scoreboard + + end2end e2e; //end to end checker from l1 cache to memory bus + + function void build_phase(uvm_phase phase); + mem_bfm = memory_bfm::type_id::create("MEM_BFM", this); + + cpu_agt = d_cpu_agent::type_id::create("CPU_AGT", this); + cpu_pred = bus_predictor::type_id::create("CPU_PRED", this); + cpu_pred.cache = new("CPU_PRED_CACHE", mem_bfm, 0); + cpu_score = bus_scoreboard::type_id::create("CPU_SCORE", this); + + mem_agt = mem_agent::type_id::create("MEM_AGT", this); + mem_pred = bus_predictor::type_id::create("MEM_PRED", this); + mem_pred.cache = new("MEM_PRED_CACHE", mem_bfm, 1); + mem_score = bus_scoreboard::type_id::create("MEM_SCORE", this); + + e2e = end2end::type_id::create("E2E", this); + e2e.cache = new("E2E_CACHE", mem_bfm, 0); + endfunction + + function void connect_phase(uvm_phase phase); + // L1 CACHE AGENT + bus_connect(cpu_agt, cpu_pred, cpu_score); + + // MEMORY AGENT + bus_connect(mem_agt, mem_pred, mem_score); + + // L1 CACHE <-> MEMORY :: END TO END CHECKER + cpu_agt.mon.req_ap.connect(e2e.src_req_export); + cpu_agt.mon.resp_ap.connect(e2e.src_resp_export); + mem_agt.mon.resp_ap.connect(e2e.dest_resp_export); + endfunction : connect_phase +`endif + +`ifdef TB_L2_CONFIG + mem_arb_agent mem_arb_agt; // contains monitor and driver + bus_predictor mem_arb_pred; // a reference model to check the result + bus_scoreboard mem_arb_score; // scoreboard + + mem_agent mem_agt; // contains monitor + bus_predictor mem_pred; // a reference model to check the result + bus_scoreboard mem_score; // scoreboard + + end2end e2e; //end to end checker from l2 cache to memory bus + + function void build_phase(uvm_phase phase); + mem_bfm = memory_bfm::type_id::create("MEM_BFM", this); + + mem_arb_agt = mem_arb_agent::type_id::create("MEM_ARB_AGT", this); + mem_arb_pred = bus_predictor::type_id::create("MEM_ARB_PRED", this); + mem_arb_pred.cache = new("MEM_ARB_PRED_CACHE", mem_bfm, 1); + mem_arb_score = bus_scoreboard::type_id::create("MEM_ARB_SCORE", this); + + mem_agt = mem_agent::type_id::create("MEM_AGT", this); + mem_pred = bus_predictor::type_id::create("MEM_PRED", this); + mem_pred.cache = new("MEM_PRED_CACHE", mem_bfm, 1); + mem_score = bus_scoreboard::type_id::create("MEM_SCORE", this); + + e2e = end2end::type_id::create("E2E", this); + e2e.cache = new("E2E_CACHE", mem_bfm, 1); + endfunction + + function void connect_phase(uvm_phase phase); + // L1 CACHE AGENT + bus_connect(mem_arb_agt, mem_arb_pred, mem_arb_score); + + // MEMORY AGENT + bus_connect(mem_agt, mem_pred, mem_score); + + // L1 CACHE <-> MEMORY :: END TO END CHECKER + mem_arb_agt.mon.req_ap.connect(e2e.src_req_export); + mem_arb_agt.mon.resp_ap.connect(e2e.src_resp_export); + mem_agt.mon.resp_ap.connect(e2e.dest_resp_export); + endfunction : connect_phase +`endif + +`ifdef TB_FULL_CONFIG + d_cpu_agent d_cpu_agt; // contains monitor and driver + bus_predictor d_cpu_pred; // a reference model to check the result + bus_scoreboard d_cpu_score; // scoreboard + + i_cpu_agent i_cpu_agt; // contains monitor and driver + bus_predictor i_cpu_pred; // a reference model to check the result + bus_scoreboard i_cpu_score; // scoreboard + + l2_agent l2_agt; // contains monitor + bus_predictor l2_pred; // a reference model to check the result + bus_scoreboard l2_score; // scoreboard + + mem_agent mem_agt; // contains monitor + bus_predictor mem_pred; // a reference model to check the result + bus_scoreboard mem_score; // scoreboard + + end2end dl1_l2_e2e; //end to end checker from data l1 cache to l2 cache + end2end il1_l2_e2e; //end to end checker from instr l1 cache to l2 cache + end2end l2_mem_e2e; //end to end checker from l2 cache to memory bus + + function void build_phase(uvm_phase phase); + mem_bfm = memory_bfm::type_id::create("MEM_BFM", this); + + d_cpu_agt = d_cpu_agent::type_id::create("D_CPU_AGT", this); + d_cpu_pred = bus_predictor::type_id::create("D_CPU_PRED", this); + d_cpu_pred.cache = new("D_CPU_PRED_CACHE", mem_bfm, 0); + d_cpu_score = bus_scoreboard::type_id::create("D_CPU_SCORE", this); + + i_cpu_agt = i_cpu_agent::type_id::create("I_CPU_AGT", this); + i_cpu_pred = bus_predictor::type_id::create("I_CPU_PRED", this); + i_cpu_pred.cache = new("I_CPU_PRED_CACHE", mem_bfm, 0); + i_cpu_score = bus_scoreboard::type_id::create("I_CPU_SCORE", this); + + l2_agt = l2_agent::type_id::create("L2_AGT", this); + l2_pred = bus_predictor::type_id::create("L2_PRED", this); + l2_pred.cache = new("L2_PRED_CACHE", mem_bfm, 1); + l2_score = bus_scoreboard::type_id::create("L2_SCORE", this); + + mem_agt = mem_agent::type_id::create("MEM_AGT", this); + mem_pred = bus_predictor::type_id::create("MEM_PRED", this); + mem_pred.cache = new("MEM_PRED_CACHE", mem_bfm, 1); + mem_score = bus_scoreboard::type_id::create("MEM_SCORE", this); + + dl1_l2_e2e = end2end::type_id::create("DL1_L2_E2E", this); + dl1_l2_e2e.cache = new("DL1_L2_E2E_CACHE", mem_bfm, 0); + + il1_l2_e2e = end2end::type_id::create("IL1_L2_E2E", this); + il1_l2_e2e.cache = new("IL1_L2_E2E_CACHE", mem_bfm, 0); + + l2_mem_e2e = end2end::type_id::create("L2_MEM_E2E", this); + l2_mem_e2e.cache = new("L2_MEM_E2E_CACHE", mem_bfm, 1); + endfunction + + function void connect_phase(uvm_phase phase); + // DATA L1 CACHE AGENT + bus_connect(d_cpu_agt, d_cpu_pred, d_cpu_score); + + // INSTRUCTION L1 CACHE AGENT + bus_connect(i_cpu_agt, i_cpu_pred, i_cpu_score); + + // MEMORY AGENT + bus_connect(mem_agt, mem_pred, mem_score); + + // DATA L1 CACHE <-> L2 CACHE :: END TO END CHECKER + d_cpu_agt.mon.req_ap.connect(dl1_l2_e2e.src_req_export); + d_cpu_agt.mon.resp_ap.connect(dl1_l2_e2e.src_resp_export); + l2_agt.mon.resp_ap.connect(dl1_l2_e2e.dest_resp_export); + + // L2 CACHE <-> MEMORY :: END TO END CHECKER + l2_agt.mon.req_ap.connect(l2_mem_e2e.src_req_export); + l2_agt.mon.resp_ap.connect(l2_mem_e2e.src_resp_export); + mem_agt.mon.resp_ap.connect(l2_mem_e2e.dest_resp_export); + endfunction : connect_phase +`endif + + function void bus_connect(bus_agent agt, bus_predictor pred, bus_scoreboard score); + agt.mon.req_ap.connect(pred.analysis_export); // connect monitor to predictor + `uvm_info(this.get_name(), $sformatf( + "Connected <%s>-req_ap to <%s>", agt.mon.get_name(), pred.get_name()), UVM_FULL) + + pred.pred_ap.connect(score.expected_export); // connect predictor to scoreboard + `uvm_info(this.get_name(), $sformatf("Connected <%s> to <%s>", pred.get_name(), score.get_name() + ), UVM_FULL) + + agt.mon.resp_ap.connect(score.actual_export); // connect monitor to scoreboard + `uvm_info(this.get_name(), $sformatf( + "Connected <%s>-resp_ap to <%s>", agt.mon.get_name(), score.get_name()), UVM_FULL) + endfunction : bus_connect + +endclass : cache_env + +`endif diff --git a/verification/uvm/caches/env/cache_env_config.svh b/verification/uvm/caches/env/cache_env_config.svh new file mode 100644 index 000000000..90a6f4fbd --- /dev/null +++ b/verification/uvm/caches/env/cache_env_config.svh @@ -0,0 +1,79 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cache_evn_config.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Environment configurable parameters +*/ + +`ifndef CACHE_ENV_CONFIG +`define CACHE_ENV_CONFIG + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +class cache_env_config extends uvm_object; + int mem_timeout; + int mem_latency; + int mmio_latency; + int iterations; + + rand logic [15:0] mem_tag; // used by mem bfm and bus predictors for non-initialized memory + rand + logic [15:0] + mmio_tag; // used by mem bfm and bus predictors for memory mapped io response to reads + + `uvm_object_utils_begin(cache_env_config) + `uvm_field_int(iterations, UVM_ALL_ON) + `uvm_field_int(mem_timeout, UVM_ALL_ON) + `uvm_field_int(mem_latency, UVM_ALL_ON) + `uvm_field_int(mmio_latency, UVM_ALL_ON) + `uvm_field_int(mem_tag, UVM_ALL_ON) + `uvm_field_int(mmio_tag, UVM_ALL_ON) + `uvm_object_utils_end + + function new(string name = "cache_env_config"); + super.new(name); + if (!uvm_config_db#(uvm_bitstream_t)::get(null, "", "mem_timeout", mem_timeout)) begin + `uvm_fatal("mem_timeout", "No parameter passed in from command line with uvm_set_config_int"); + end + + if (!uvm_config_db#(uvm_bitstream_t)::get(null, "", "mem_latency", mem_latency)) begin + `uvm_fatal("mem_latency", "No parameter passed in from command line with uvm_set_config_int"); + end + + if (!uvm_config_db#(uvm_bitstream_t)::get(null, "", "mmio_latency", mmio_latency)) begin + `uvm_fatal("mmio_latency", + "No parameter passed in from command line with uvm_set_config_int"); + end + + if (!uvm_config_db#(uvm_bitstream_t)::get(null, "", "iterations", iterations)) begin + `uvm_fatal("iterations", "No parameter passed in from command line with uvm_set_config_int"); + end + endfunction + + function void post_randomize(); + `uvm_info(this.get_name(), $sformatf("env_config params:\n%s", this.sprint()), UVM_LOW) + endfunction : post_randomize + +endclass : cache_env_config + +`endif diff --git a/verification/uvm/caches/env/dut_params.svh b/verification/uvm/caches/env/dut_params.svh new file mode 100644 index 000000000..a8f35435c --- /dev/null +++ b/verification/uvm/caches/env/dut_params.svh @@ -0,0 +1,59 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: dut_params.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Hard coded configuration parameters for the DUT +*/ + +`ifndef DUT_PARAMS_SVH +`define DUT_PARAMS_SVH + +// time in ns +`define CLK_PERIOD 10 +// time in ps +`define PROPAGATION_DELAY #(2000); +// time in ps +`define MONITOR_DELAY #(3000); + +`define NONCACHE_START_ADDR 32'h8000_0000 + +//32 bit addr - word offset - byte offset +`define BYTE_INDEX_BITS 2 + +// L2 Params +`define L1_BLOCK_SIZE 4 +`define L1_CACHE_SIZE 2048 +`define L1_ASSOC 2 + +`define L1_WORD_INDEX_BITS ($clog2(`L1_BLOCK_SIZE)) +`define L1_FRAME_INDEX_BITS ($clog2((`L1_CACHE_SIZE / (32 / 8) ) / `L1_ASSOC / `L1_BLOCK_SIZE)) +`define L1_INDEX_BITS (`L1_FRAME_INDEX_BITS + `L1_WORD_INDEX_BITS + `BYTE_INDEX_BITS) +`define L1_TAG_BITS (32 - `L1_INDEX_BITS) + +`define L1_ADDR_IDX_END (`BYTE_INDEX_BITS+`L1_WORD_INDEX_BITS) +`define L1_ADDR_IDX_SIZE (32 - `L1_ADDR_IDX_END) + +// L2 Params + +`define L2_CACHE_SIZE 4096 +`define L2_BLOCK_SIZE 4 +`define L2_ASSOC 4 + +`endif diff --git a/verification/uvm/caches/env/interface_checker.svh b/verification/uvm/caches/env/interface_checker.svh new file mode 100644 index 000000000..770b0cd04 --- /dev/null +++ b/verification/uvm/caches/env/interface_checker.svh @@ -0,0 +1,74 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: interface_checker.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Checks for invalid combinations of input/output signals for the DUT +*/ + +`ifndef INTERFACE_CHECKER_SVH +`define INTERFACE_CHECKER_SVH + +module interface_checker ( + cache_if.cache d_cif, + cache_if.cache i_cif, + cache_if.cache l2_cif, + generic_bus_if.generic_bus d_cpu_if, + generic_bus_if.generic_bus i_cpu_if, + generic_bus_if.generic_bus d_l1_arb_bus_if, + generic_bus_if.generic_bus i_l1_arb_bus_if, + generic_bus_if.generic_bus arb_l2_bus_if, + generic_bus_if.generic_bus mem_if +); + + // Flush done without a flush request + d_cif_flush_done : + assert property (@(posedge d_cif.CLK) d_cif.flush_done |-> (d_cif.flush)) + else $fatal(1, "'d_cif.flush_done' should never be asserted without a cpu flush request"); + i_cif_flush_done : + assert property (@(posedge i_cif.CLK) i_cif.flush_done |-> (i_cif.flush)) + else $fatal(1, "'i_cif.flush_done' should never be asserted without a cpu flush request"); + l2_cif_flush_done : + assert property (@(posedge l2_cif.CLK) l2_cif.flush_done |-> (l2_cif.flush)) + else $fatal(1, "'l2_cif.flush_done' should never be asserted without a cpu flush request"); + + // L2 Response without a read/write request + arb_l2_bus_if_busy : + assert property (@(posedge l2_cif.CLK) !arb_l2_bus_if.busy |-> (arb_l2_bus_if.ren | arb_l2_bus_if.wen)) + else $fatal(1, "'arb_l2_bus_if.busy' should never be low without a cpu read/write request"); + + // Ensure all bus transactions only occur when there is a read/write request + d_l1_arb_bus_if_ren : + assert property (@(posedge d_cif.CLK) d_l1_arb_bus_if.ren |-> (!d_cpu_if.ren)) + else $fatal(1, "'d_l1_arb_bus_if.ren' should never be asserted without a cpu read request"); + d_l1_arb_bus_if_wen : + assert property (@(posedge d_cif.CLK) d_l1_arb_bus_if.wen |-> (!d_cpu_if.wen)) + else $fatal(1, "'d_l1_arb_bus_if.wen' should never be asserted without a cpu write request"); + + i_l1_arb_bus_if_ren : + assert property (@(posedge d_cif.CLK) i_l1_arb_bus_if.ren |-> (i_cpu_if.ren)) + else $fatal(1, "'i_l1_arb_bus_if.ren' should never be asserted without a cpu read request"); + i_l1_arb_bus_if_wen : + assert property (@(posedge d_cif.CLK) i_l1_arb_bus_if.wen |-> (i_cpu_if.wen)) + else $fatal(1, "'i_l1_arb_bus_if.wen' should never be asserted without a cpu write request"); + + //TODO: IMPLEMENT CHECK FOR INVALID BYTE_EN +endmodule : interface_checker + +`endif diff --git a/verification/uvm/caches/generic_bus_agent_comps/bus_agent.svh b/verification/uvm/caches/generic_bus_agent_comps/bus_agent.svh new file mode 100644 index 000000000..a3e14d77d --- /dev/null +++ b/verification/uvm/caches/generic_bus_agent_comps/bus_agent.svh @@ -0,0 +1,55 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: bus_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: Generic UVM Agent to for a generic_bus_if +*/ + +`ifndef BUS_AGENT_SVH +`define BUS_AGENT_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "cpu_driver.svh" +`include "bus_monitor.svh" +`include "cpu_sequencer.svh" + +class null_driver; +endclass + +class bus_agent #( + type driver = null_driver +) extends uvm_agent; + `uvm_component_param_utils(bus_agent#(driver)) + cpu_sequencer sqr; + driver drv; + bus_monitor mon; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + +endclass : bus_agent + +`endif diff --git a/verification/uvm/caches/generic_bus_agent_comps/bus_monitor.svh b/verification/uvm/caches/generic_bus_agent_comps/bus_monitor.svh new file mode 100644 index 000000000..33ea587c2 --- /dev/null +++ b/verification/uvm/caches/generic_bus_agent_comps/bus_monitor.svh @@ -0,0 +1,168 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: bus_monitor.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Monitor class for monitoring a generic_bus_if +*/ + +`ifndef BUS_MONITOR_SVH +`define BUS_MONITOR_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" + +`include "generic_bus_if.vh" +`include "cache_if.svh" +`include "dut_params.svh" + +class bus_monitor extends uvm_monitor; + // precedence breaks ties for transactions that come during the same tick (lower is higher precedence) + // TODO: change precedence to using uvm_wait_for_nba_region() + `uvm_component_utils(bus_monitor) + + virtual cache_if cif; + virtual generic_bus_if bus_if; + + cache_env_config env_config; + + uvm_analysis_port #(cpu_transaction) req_ap; + uvm_analysis_port #(cpu_transaction) resp_ap; + + int cycle; // number of clock cycles that have elapsed + + int precedence; + string cif_str; + string bus_if_str; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + req_ap = new("req_ap", this); + resp_ap = new("resp_ap", this); + precedence = 0; + cif_str = ""; + bus_if_str = ""; + endfunction : new + + function void set_cif_str(string str); + this.cif_str = str; + endfunction : set_cif_str + + function void set_bus_if_str(string str); + this.bus_if_str = str; + endfunction : set_bus_if_str + + function void set_precedence(int p); + this.precedence = p; + endfunction : set_precedence + + // Build Phase - Get handle to virtual if from config_db + virtual function void build_phase(uvm_phase phase); + super.build_phase(phase); + // get config from database + if (!uvm_config_db#(cache_env_config)::get(this, "", "env_config", env_config)) begin + `uvm_fatal(this.get_name(), "env config not registered to db") + end + `uvm_info(this.get_name(), "pulled from db", UVM_FULL) + + if (!uvm_config_db#(virtual cache_if)::get(this, "", cif_str, cif)) begin + `uvm_fatal($sformatf("%s/%s", this.get_name(), cif_str), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), $sformatf("pulled <%s> from db", cif_str), UVM_FULL) + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", bus_if_str, bus_if)) begin + `uvm_fatal($sformatf("%s/%s", this.get_name(), bus_if_str), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), $sformatf("pulled <%s> from db", bus_if_str), UVM_FULL) + endfunction : build_phase + + virtual task run_phase(uvm_phase phase); + super.run_phase(phase); + + forever begin + cpu_transaction tx; + + @(posedge cif.CLK); + `MONITOR_DELAY // delay to pick up new value from bus + + if (cif.flush) begin + tx = cpu_transaction::type_id::create("tx"); + tx.addr = bus_if.addr; + tx.data = 'x; + tx.byte_en = bus_if.byte_en; + tx.flush = cif.flush; + + `uvm_info(this.get_name(), $sformatf("Writing Req AP:\nReq Ap:\n%s", tx.sprint()), UVM_FULL) + req_ap.write(tx); + + mem_wait(1'b1, cif.flush_done); + + `uvm_info(this.get_name(), $sformatf("Writing Resp AP:\nReq Ap:\n%s", tx.sprint()), + UVM_FULL) + resp_ap.write(tx); + end else if (bus_if.ren || bus_if.wen) begin + // captures activity between the driver and DUT + tx = cpu_transaction::type_id::create("tx"); + + tx.addr = bus_if.addr; + tx.byte_en = bus_if.byte_en; + tx.flush = cif.flush; + + if (bus_if.ren) begin + tx.rw = '0; // 0 -> read; 1 -> write + tx.data = 'x; //fill with garbage data + end else if (bus_if.wen) begin + tx.rw = '1; // 0 -> read; 1 -> write + tx.data = bus_if.wdata; + end + + `uvm_info(this.get_name(), $sformatf("Writing Req AP:\nReq Ap:\n%s", tx.sprint()), UVM_FULL) + req_ap.write(tx); + + mem_wait(1'b0, bus_if.busy); + + if (bus_if.ren) begin + tx.data = bus_if.rdata; + end + + #(precedence); + `uvm_info(this.get_name(), $sformatf("Writing Resp AP:\nReq Ap:\n%s", tx.sprint()), + UVM_FULL) + resp_ap.write(tx); + end + end + endtask : run_phase + + task mem_wait(logic clear, const ref logic flag); + int cycle = 0; + while (flag != clear) begin + @(posedge cif.CLK); + `MONITOR_DELAY // delay to pick up new value from bus + cycle++; //wait for memory to return + if (cycle > env_config.mem_timeout) begin + `uvm_fatal(this.get_name(), "memory timeout reached") + end + end + endtask : mem_wait + +endclass : bus_monitor + +`endif diff --git a/verification/uvm/caches/generic_bus_agent_comps/bus_predictor.svh b/verification/uvm/caches/generic_bus_agent_comps/bus_predictor.svh new file mode 100644 index 000000000..901fe060c --- /dev/null +++ b/verification/uvm/caches/generic_bus_agent_comps/bus_predictor.svh @@ -0,0 +1,106 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: bus_predictor.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM subscriber class for predicting the operation of a generic_bus_if +*/ + +`ifndef BUS_PREDICTOR_SHV +`define BUS_PREDICTOR_SHV + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "cpu_transaction.svh" +`include "cache_model.svh" + +class bus_predictor extends uvm_subscriber #(cpu_transaction); + `uvm_component_utils(bus_predictor) + + uvm_analysis_port #(cpu_transaction) pred_ap; + cpu_transaction pred_tx; + + cache_env_config env_config; + + cache_model cache; //software cache + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction : new + + function void build_phase(uvm_phase phase); + + pred_ap = new("pred_ap", this); + + // get config from database + if (!uvm_config_db#(cache_env_config)::get(this, "", "env_config", env_config)) begin + `uvm_fatal(this.get_name(), "env config not registered to db") + end + + endfunction + + function void write(cpu_transaction t); + // t is the transaction sent from monitor + pred_tx = cpu_transaction::type_id::create("pred_tx", this); + pred_tx.copy(t); + + `uvm_info(this.get_name(), $sformatf("Recevied Transaction:\n%s", pred_tx.sprint()), UVM_HIGH) + + `uvm_info(this.get_name(), $sformatf("cache before:\n%s", cache.sprint()), UVM_HIGH) + + if (pred_tx.flush) begin + pred_ap.write(pred_tx); // flush doesn't return any data + // don't update cache model because we need reads to return same data as was written + end else begin + // no cache flush + if (pred_tx.rw) begin + // 1 -> write + if (pred_tx.addr < `NONCACHE_START_ADDR) begin + if (cache.exists(pred_tx.addr)) begin + cache.update(pred_tx.addr, pred_tx.data, pred_tx.byte_en); + end else begin + cache.insert(pred_tx.addr, pred_tx.data, pred_tx.byte_en); + end + end + end else begin + // 0 -> read + if (pred_tx.addr < `NONCACHE_START_ADDR) begin + // cache/cache responds + pred_tx.data = cache.read(pred_tx.addr); + end else begin + // mmio responds + pred_tx.data = {env_config.mmio_tag, pred_tx.addr[15:0]}; + `uvm_info( + this.get_name(), $sformatf( + "Reading from Memory Mapped Address Space, Defaulting to value <%h>", pred_tx.data), + UVM_MEDIUM) + end + end + // after prediction, the expected output send to the scoreboard + pred_ap.write(pred_tx); + end + + `uvm_info(this.get_name(), $sformatf("cache after:\n%s", cache.sprint()), UVM_HIGH) + endfunction : write + +endclass : bus_predictor + +`endif diff --git a/verification/uvm/caches/generic_bus_agent_comps/bus_scoreboard.svh b/verification/uvm/caches/generic_bus_agent_comps/bus_scoreboard.svh new file mode 100644 index 000000000..ceb4f0b22 --- /dev/null +++ b/verification/uvm/caches/generic_bus_agent_comps/bus_scoreboard.svh @@ -0,0 +1,97 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: bus_scoreboard.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Scoreboard class for checking the operation of a generic_bus_if +*/ + +`ifndef BUS_SCOREBOARD_SVH +`define BUS_SCOREBOARD_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" + +class bus_scoreboard extends uvm_scoreboard; + `uvm_component_utils(bus_scoreboard) + uvm_analysis_export #(cpu_transaction) expected_export; // receive result from predictor + uvm_analysis_export #(cpu_transaction) actual_export; // receive result from DUT + uvm_tlm_analysis_fifo #(cpu_transaction) expected_fifo; + uvm_tlm_analysis_fifo #(cpu_transaction) actual_fifo; + + int m_matches, m_mismatches; // records number of matches and mismatches + + function new(string name, uvm_component parent); + super.new(name, parent); + m_matches = 0; + m_mismatches = 0; + endfunction + + function void build_phase(uvm_phase phase); + expected_export = new("expected_export", this); + actual_export = new("actual_export", this); + expected_fifo = new("expected_fifo", this); + actual_fifo = new("actual_fifo", this); + endfunction + + function void connect_phase(uvm_phase phase); + expected_export.connect(expected_fifo.analysis_export); + actual_export.connect(actual_fifo.analysis_export); + endfunction + + task run_phase(uvm_phase phase); + cpu_transaction expected_tx; //transaction from predictor + cpu_transaction actual_tx; //transaction from DUT + forever begin + expected_fifo.get(expected_tx); + `uvm_info(this.get_name(), $sformatf("Recieved new expected value:\n%s", + expected_tx.sprint()), UVM_HIGH); + + actual_fifo.get(actual_tx); + `uvm_info(this.get_name(), $sformatf("Recieved new actual value:\n%s", actual_tx.sprint()), + UVM_HIGH); + + if (expected_tx.compare(actual_tx)) begin + m_matches++; + `uvm_info(this.get_name(), "Success: Data Match", UVM_LOW); + `uvm_info(this.get_name(), $sformatf("\nExpected:\n%s\nReceived:\n%s", + expected_tx.sprint(), actual_tx.sprint()), UVM_MEDIUM) + end else begin + m_mismatches++; + `uvm_error(this.get_name(), "Error: Data Mismatch"); + `uvm_info(this.get_name(), $sformatf( + "\nExpected:\n%s\nReceived:\n%s", expected_tx.sprint(), actual_tx.sprint()), + UVM_LOW) + end + end + endtask + + + + function void report_phase(uvm_phase phase); + `uvm_info($sformatf("%s", this.get_name()), $sformatf("Matches: %0d", m_matches), UVM_LOW); + `uvm_info($sformatf("%s", this.get_name()), $sformatf("Mismatches: %0d", m_mismatches), + UVM_LOW); + `uvm_info($sformatf("%s", this.get_name()), $sformatf("TXN_Total: %0d", + m_matches + m_mismatches), UVM_LOW); + endfunction + +endclass : bus_scoreboard + +`endif diff --git a/verification/uvm/caches/models/Utils.svh b/verification/uvm/caches/models/Utils.svh new file mode 100644 index 000000000..20bd36eb5 --- /dev/null +++ b/verification/uvm/caches/models/Utils.svh @@ -0,0 +1,43 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: utils.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/1/2022 +* Description: utility class for: +* - helping with byte_en masking +*/ + +`ifndef UTILS_SHV +`define UTILS_SHV + +class Utils; + static function word_t byte_mask(logic [3:0] byte_en); + word_t mask; + + mask = '0; + for (int i = 0; i < 4; i++) begin + if (byte_en[i]) begin + mask |= 32'hff << (8 * i); + end + end + return mask; + endfunction : byte_mask +endclass : Utils + +`endif diff --git a/verification/uvm/caches/models/cache_model.svh b/verification/uvm/caches/models/cache_model.svh new file mode 100644 index 000000000..daaa0432b --- /dev/null +++ b/verification/uvm/caches/models/cache_model.svh @@ -0,0 +1,240 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cache_model.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Abstracted software model of the caches +*/ + +`ifndef CACHE_MODEL_SVH +`define CACHE_MODEL_SVH + +`include "dut_params.svh" +`include "memory_bfm.svh" +`include "Utils.svh" + +import uvm_pkg::*; +import rv32i_types_pkg::*; + + +typedef enum { + CLEAN, + DIRTY +} cache_state; + + +/****************** Cache Block Model **********************/ + +class cache_block; + word_t words[word_t]; // key -> value :: addr -> data + cache_state state; + + function bit size_check(); + if (words.size() >= `L1_BLOCK_SIZE) return '0; + else return '1; + endfunction : size_check + + function void insert(word_t addr, word_t data); + if (!size_check()) begin + `uvm_error( + "cache_block", + $sformatf( + "Block overflow when attempting to insert item into cache: size %d:\ncache[%h]=%h", + words.size(), addr, data)); + end + if (words.exists(addr)) begin + `uvm_error("cache_block", + $sformatf("Attempted to insert item into cache that already exists:\ncache[%h]=%h", + addr, data)) + end else begin + words[addr] = data; + `uvm_info("cache_block", $sformatf("inserted cache word, cache[%h] = %h", addr, data), + UVM_MEDIUM) + end + endfunction : insert + + function void update(word_t addr, word_t data); + if (!words.exists(addr)) begin + `uvm_error("cache_block", $sformatf( + "Attempted to update item into cache that DNE:\ncache[%h]=%h", + addr, data)) + end else begin + words[addr] = data; + state = DIRTY; + `uvm_info("cache_block", $sformatf("updated cache word, cache[%h] = %h", addr, data), + UVM_MEDIUM) + end + endfunction : update + + function bit dirty(); + return this.state == DIRTY; + endfunction : dirty + + function string sprint(); + string str = "["; + foreach (words[w]) begin + str = {str, $sformatf("%h, ", words[w])}; + end + str = {str, $sformatf("] - %s\n", this.state.name)}; + return str; + endfunction : sprint +endclass + +/******************** Cache Model *************************/ + +class cache_model extends uvm_object; + cache_block blocks[word_t]; // key -> value :: base addr -> block + + memory_bfm bfm; + + bit ignore_mask; + + function new(string name = "cache_model", memory_bfm bfm, bit ignore_mask); + super.new(name); + this.bfm = bfm; + this.ignore_mask = ignore_mask; + endfunction + + function bit dirty(); + foreach (blocks[addr]) begin + if (blocks[addr].dirty()) begin + return 1'b1; + end + end + + return 1'b0; + endfunction : dirty + + function word_t get_base_addr(word_t addr); + word_t base = {addr[31:`L1_ADDR_IDX_END], {`L1_ADDR_IDX_END{1'b0}}}; + return base; + endfunction : get_base_addr + + function void insert(word_t addr, word_t data, logic [3:0] byte_en); + word_t base = get_base_addr(addr); + + if (exists(addr)) begin + // cache block formation started + `uvm_error(this.get_name(), + $sformatf("Attempted to insert item from cache that already exists:\ncache[%h]=%h", + addr, data)) + end else begin + word_t m_data; + word_t mask = Utils::byte_mask(byte_en); + if (ignore_mask) begin + m_data = data; + end else begin + m_data = (mask & data) | (~mask & read(addr)); + end + if (!blocks.exists(base)) begin + blocks[base] = new(); + end + blocks[base].insert(addr, m_data); + end + endfunction : insert + + function bit remove(word_t addr, word_t data); + if (exists(addr)) begin + word_t base = get_base_addr(addr); + blocks[base].words.delete(addr); + + if (!blocks[base].dirty()) begin + `uvm_error(this.get_name(), + $sformatf("Attempted to remove item from cache that is not dirty:\ncache[%h]=%h", + addr, data)) + return 0; + end + end else begin + `uvm_error(this.get_name(), $sformatf( + "Attempted to remove item from cache that DNE:\ncache[%h]=%h", addr, data)) + return 0; + end + return 1; + endfunction : remove + + function void update(word_t addr, word_t data, logic [3:0] byte_en); + if (exists(addr)) begin + word_t m_data; + word_t base = get_base_addr(addr); + word_t mask = Utils::byte_mask(byte_en); + if (ignore_mask) begin + m_data = data; + end else begin + m_data = (mask & data) | (~mask & read(addr)); + end + blocks[base].update(addr, m_data); + end else begin + `uvm_error(this.get_name(), $sformatf( + "Attempted to update item from cache that DNE:\ncache[%h]=%h", addr, data)) + end + endfunction : update + + function word_t read(word_t addr); + if (this.exists(addr)) begin + // check if data is cached + word_t base = get_base_addr(addr); + `uvm_info(this.get_name(), $sformatf("Reading from Initialized Data, value <%h>", + blocks[base].words[addr]), UVM_MEDIUM) + return blocks[base].words[addr]; + end else begin + // otherwise get expected value from bfm + word_t default_val = bfm.read(addr); + `uvm_info(this.get_name(), $sformatf( + "Reading from Non-Initialized Data, Defaulting to value <%h>", default_val), + UVM_MEDIUM) + return default_val; + end + endfunction : read + + function bit exists(word_t addr); + word_t base = get_base_addr(addr); + return blocks.exists(base) && blocks[base].words.exists(addr); + endfunction : exists + + function bit is_valid_block(word_t addr); + word_t base = get_base_addr(addr); + if (blocks.exists(base)) begin + if (blocks[base].words.size() == 0) begin + blocks.delete(base); // clean up evicted blocks + return '1; + end else begin + return blocks[base].words.size() == `L1_BLOCK_SIZE; // ensure block is full + end + end else begin + `uvm_info(this.get_name(), $sformatf("Unable to find block %h for requested addr: %h", base, + addr), UVM_LOW); + return '0; + end + endfunction : is_valid_block + + function void flush(); + blocks.delete(); // clear all data in cache + endfunction : flush + + function string sprint(); + string str = "cache:\n"; + foreach (blocks[b]) begin + str = {str, $sformatf("%h :: %s", b, blocks[b].sprint())}; + end + return str; + endfunction : sprint + +endclass : cache_model + +`endif diff --git a/verification/uvm/caches/readme.md b/verification/uvm/caches/readme.md new file mode 100644 index 000000000..29479e248 --- /dev/null +++ b/verification/uvm/caches/readme.md @@ -0,0 +1,89 @@ +# Caches UVM Testbench Setup Guide + +### .bashrc +> Note that there may be some env variables listed here that are not required. +``` +source ~/init_vlsi -p mitll90_Dec2019 -e all +export MGC_FDI_OA_VERSION=22.50 +export SOCET_FILES="/home/ecegrid/a/socpub/Public" +# WAF Build System +export SFF_ADMIN=$SOCET_FILES/SoCFoundationFlow/admin +source $SFF_ADMIN/setup_env.bash +export SFF_SIM_ENV="incisive" +# RISCV Setup +export TOOLS=$SOCET_FILES/riscv_dev +export RISCV=$TOOLS/riscv_installs/RV_current +export PATH=$TOOLS/scripts\:$PATH +export PATH=$RISCV/bin\:$PATH +export PATH=/opt/gcc/5.3.0/bin\:$PATH +export LD_LIBRARY_PATH=$RISCV/lib\:/opt/gcc/5.3.0/lib64 +export LIBRARY_PATH=/opt/gcc/5.3.0/lib64 +export PYTHONPATH=$TOOLS/python_libs/lib64/python +export PERL5LIB=$TOOLS/perl_libs/installs/Verilog-Perl/lib/perl5 +export PATH=$PATH\:$TOOLS/perl_libs/installs/Verilog-Perl/bin/ +# Various system variables +export SOCROOT="$HOME/SoCET_Public" +export PATH=/package/eda/cadence/GENUS191/tools/bin:$PATH +export QUESTA_HOME=/package/eda/mg/questa10.6b/questasim +#load newer version of git +module load git +``` + +### Build/Run Params +Everything related to building and running the uvm testbench is handled by the run.py script. To view the parameters: +```bash +run.py -h +``` +`Note:` you may need to change permissions of the run.py file: +```bash +chmod u+x run.py +``` + +## How to Debug DUT +The best way to use this UVM testbench for debugging is to utilize a combination of the `transcript` file and the waveforms. + +First run the design with the desired test configuration in gui mode: +``` +run.py -g --config l2 -s 12345 +``` +`Note`: when debugging it is helpful to have a static/non-random test for consistency. This is why I have added the `-s 12345` flag here. This is optional. + +This command will invoke the QuestaSim Gui and will auto load the waveforms for the correct config. Make sure to answer `no` to the `Are you sure you want to finish?` prompt. You can now search through the `transcript` file for any errors (Ctrl + f for "error"). You can now view the error and know the time step where the error occurred on the waveform. + +Let's walk through an example: +```bash +# 753000: uvm_test_top.ENV.MEM_ARB_SCORE [MEM_ARB_SCORE] Error: Data Mismatch +# UVM_INFO generic_bus_agent_comps/bus_scoreboard.svh(75) @ 753000: uvm_test_top.ENV.MEM_ARB_SCORE [MEM_ARB_SCORE] +# Expected: +# -------------------------------------------- +# Name Type Size Value +# -------------------------------------------- +# pred_tx cpu_transaction - @1297 +# rw integral 1 'h0 +# addr integral 32 'h2dc2bc5c +# data integral 32 'hced4bc5c +# byte_en integral 4 'h3 +# -------------------------------------------- +# +# Received: +# -------------------------------------------- +# Name Type Size Value +# -------------------------------------------- +# tx cpu_transaction - @1289 +# rw integral 1 'h0 +# addr integral 32 'h2dc2bc5c +# data integral 32 'hced8d351 +# byte_en integral 4 'h3 +# -------------------------------------------- +# +``` + +We see that the first line gives an error message and the time step, in this case at time 753000 there was a mismatch in data from the memory arbiter (MEM_ARB_SCORE). We also have the expected and actual values from the test. Most of the fields of each transaction are straight forward to understand except for `rw`. This value indicates if the transaction is a read or a write. If `rw == 1`, it was a `write` request, `otherwise`, it was a `read`. With this information the design engineering is armed with great information to begin reading through the waveforms to determine the cause of the issue. + +## Design Notes: +- Need to drive byte_en to memory, at least full word (4'b1000) +- evicting the right data but wrong address + +## Extension Ideas: +- Timing Agent + - responsible for monitoring both buses like end2end and checking if the correct number of cycles for hits/misses \ No newline at end of file diff --git a/verification/uvm/caches/run.py b/verification/uvm/caches/run.py new file mode 100755 index 000000000..0e9ed5680 --- /dev/null +++ b/verification/uvm/caches/run.py @@ -0,0 +1,132 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: run.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/04/2022 +# Description: Script for configuring and running UVM TB for the caches + +import argparse +import os +from scripts.cprint import cprint +from scripts.cprint import csprint +from scripts.cprint import tags, styles +from scripts.build import build +from scripts.run import run +from scripts.post_run import post_run +from scripts.repeat import repeat + +def seed_type(arg): + try: + return int(arg) # try convert to int + except ValueError: + pass + if arg == "random": + return arg + raise argparse.ArgumentTypeError("Seed must be an integer type or 'random'") + +def parse_arguments(): + parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, + description=csprint("Build and Run the UVM Testbench for the cache hierarchy\n" + " Note that all runtime parameters are saved in ", + styles.PURPLE, styles.BOLD) + + csprint("run_summary.log", styles.PURPLE, styles.BOLD, styles.UNDERLINE) + ) + parser.add_argument('--clean', action="store_true", + help=csprint("Remove build artifacts", styles.BLUE)) + parser.add_argument('--build', action="store_true", + help=csprint("Build project without run", styles.BLUE)) + parser.add_argument('--repeat', action="store_true", + help=csprint("Run with the last (most recent) parameters stored in run_summary.log", styles.BLUE)) + parser.add_argument('--testcase', '-t', type=str, default="random", + choices=["nominal", "evict", "index", "mmio", "flush", "random"], + help=csprint("Specify name of the uvm test:\n", styles.YELLOW) + + " nominal: read back values previously written to caches\n" + " evict: write to same index with different tag bits to force cache eviction\n" + " index: read/write to same block of data to ensure proper block indexing\n" + " mmio: read/write to memory mapped address space\n" + " flush: perform cache flush after nominal read/writes\n" + " random: random interleaving of previous test cases" + ) + parser.add_argument('--gui', '-g', action='store_true', + help=csprint("Specify whether to run with gui or terminal only", styles.YELLOW)) + parser.add_argument('--verbosity', '-v', type=str, default="low", + choices=["none", "low", "medium", "high", "full", "debug"], + help=csprint("Specify the verbosity level to be used for UVM Logging, each stage builds on the next\n", styles.YELLOW) + + " none: - only error messages shown\n" + " low: - actual and expected values for scoreboard errors\n" + " - success msg for data matches\n" + " - sequence parameters\n" + " medium - actual and expected values for all scoreboard checks\n" + " - predictor default values for non-initialized memory\n" + " - end2end transaction/propagation details\n" + " high - all uvm transactions detected in monitors, predictors, scoreboards\n" + " - predictor memory before:after\n" + " full - all connections between analysis ports\n" + " - all agent sub-object instantiations\n" + " - all virtual interface accesses to uvm db\n" + " debug - all messages") + parser.add_argument('--seed', '-s', type=seed_type, default="random", + help=csprint("Specify starter seed for uvm randomization\n", styles.YELLOW) + + "Identical seeds will produce identical runs") + parser.add_argument('--iterations', '-i', type=int, default=0, + help=csprint("Specify the requested number of memory accesses for a test", styles.YELLOW)) + parser.add_argument('--no-if-check', action="store_true", + help=csprint("Remove interface checks from test", styles.YELLOW)) + parser.add_argument('--mem-timeout', type=int, default=1000, + help=csprint("Specify the max memory latency before a fatal timeout error", styles.YELLOW)) + parser.add_argument('--mem-latency', type=int, default=1, + help=csprint("Specify the number of clock cycles before main memory returns", styles.YELLOW)) + parser.add_argument('--mmio-latency', type=int, default=2, + help=csprint("Specify the number of clock cycles before memory mapped IO returns", styles.YELLOW)) + parser.add_argument('--config', type=str, default="full", + choices=["l1", "l2", "full"], + help=csprint("Specify the configuration of the testbench to determine which agents and modules are activated", styles.YELLOW)) + return parser.parse_args() + + +if __name__ == '__main__': + params = parse_arguments() + + if params.clean: + cprint("Cleaning Directory...", tags.LOG) + os.system("rm -rf *.vstf work mitll90_Dec2019_all covhtmlreport *.log transcript *.wlf coverage/*.ucdb **/*.pyc") + exit() + elif params.repeat: + repeat(params) + + build(params) + + if (params.build): + exit() # stop after build + + run(params) + + cprint("Run Parameters:", tags.LOG) + + # print parameters + keep = ["mem_timeout", "iterations", "mem_latency", "testcase", "config", "mmio_latency"] + for arg in vars(params): + if arg in keep: + cprint("{key:<15}<- {val}".format(key=arg, val=getattr(params, arg)), tags.INFO) + + cprint("Running Post Run Script...", tags.LOG) + + post_run(params) \ No newline at end of file diff --git a/verification/uvm/caches/scripts/__init__.py b/verification/uvm/caches/scripts/__init__.py new file mode 100644 index 000000000..66245bbd4 --- /dev/null +++ b/verification/uvm/caches/scripts/__init__.py @@ -0,0 +1,24 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: __init__.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Required for python module imports \ No newline at end of file diff --git a/verification/uvm/caches/scripts/build.py b/verification/uvm/caches/scripts/build.py new file mode 100644 index 000000000..5a4ccc218 --- /dev/null +++ b/verification/uvm/caches/scripts/build.py @@ -0,0 +1,74 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: build.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Script for building UVM TB for the caches + +import os + +from cprint import cprint +from cprint import tags + +def build(params): + cprint("Building Sources...", tags.LOG) + SRC = "../../../source_code/" + + TB_GLOBAL_CONFIG = "TB_{}_CONFIG".format(params.config) + + res = os.system('''\ + vlog\ + +incdir+{CACHES} \ + +incdir+{L1} \ + +incdir+{L2} \ + +incdir+{INCLUDE} \ + +incdir+{PACKAGES} \ + +incdir+models \ + +incdir+cpu_agent \ + +incdir+bus_agents \ + +incdir+end2end \ + +incdir+generic_bus_agent_comps \ + +incdir+bfm \ + +incdir+env \ + +incdir+sequences \ + +incdir+tests \ + +define+TB_{TB_GLOBAL_CONFIG}_CONFIG \ + +define+INTERFACE_CHECKER={IF_CHECKER} \ + +acc \ + +cover \ + -L {QUESTA_HOME}/uvm-1.2 {SRAM} tb_caches_top.sv + '''.format( + CACHES=SRC + "caches", + L1=SRC + "caches/l1", + L2=SRC + "caches/l2", + SRAM=SRC + "caches/sram/sram.sv", + INCLUDE=SRC + "include", + PACKAGES=SRC + "packages", + QUESTA_HOME=os.getenv('QUESTA_HOME'), + TB_GLOBAL_CONFIG=params.config.upper(), + IF_CHECKER="0" if params.no_if_check else "1" + )) + + if (res == 0): + cprint("Build Finished", tags.SUCCESS) + else: + cprint("Build Failed", tags.FAIL) + exit() \ No newline at end of file diff --git a/verification/uvm/caches/scripts/cprint.py b/verification/uvm/caches/scripts/cprint.py new file mode 100644 index 000000000..e77683e1b --- /dev/null +++ b/verification/uvm/caches/scripts/cprint.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: cprint.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Script for colored printing to terminal + +class styles: + PURPLE = '\033[95m' + BLUE = '\033[94m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + ENDC = '\033[0m' + +class tags: + LOG = '{}[{:<7}]:'.format(styles.PURPLE, "LOG") + INFO = '{}[{:<7}]:'.format(styles.BLUE, "INFO") + SUCCESS = '{}[{:<7}]:'.format(styles.GREEN, "SUCCESS") + WARNING = '{}[{:<7}]:'.format(styles.YELLOW, "WARNING") + FAIL = '{}[{:<7}]:'.format(styles.RED, "FAIL") + +def csprint(msg, *formats): + res = "" + for f in formats: + res += f + res += msg + res += styles.ENDC + return res + +def cprint(msg, *formats): + for f in formats: + print(f), + print(msg), + print(styles.ENDC) \ No newline at end of file diff --git a/verification/uvm/caches/scripts/post_run.py b/verification/uvm/caches/scripts/post_run.py new file mode 100644 index 000000000..e16e23bbc --- /dev/null +++ b/verification/uvm/caches/scripts/post_run.py @@ -0,0 +1,130 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: post-run.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Script for parsing run results + +from datetime import datetime + +from cprint import cprint, csprint, tags, styles + +def post_run(params): + + if (params.gui): + resp = raw_input(csprint("Save run to run_summary.log?(Y/n)", styles.YELLOW)) + if (resp == "n"): + exit() + if params.config == "l1": + keys = ["seed", "cpu_txns", "mem_txns", "uvm_error", "uvm_fatal"] # keys to log variable + elif params.config == "l2": + keys = ["seed", "mem_arb_txns", "mem_txns", "uvm_error", "uvm_fatal"] # keys to log variable + elif params.config == "full": + keys = ["seed", "d_cpu_txns", "i_cpu_txns", "mem_txns", "uvm_error", "uvm_fatal"] # keys to log variable + + log = {} + if (params.seed != "random"): + log["seed"] = params.seed + + with open("transcript", "r") as transcript: + lines = transcript.readlines() + try: + sim_err = lines[-1].split(",")[0].split(":")[1].strip() + sim_err = int(sim_err) + if (sim_err > 0): + cprint("Fatal Simulation Error Detected", tags.FAIL) + cprint("For more details, view " + csprint("verification/uvm/caches/transcript", styles.UNDERLINE), tags.FAIL) + # exit() + except Exception as err: + cprint("Unable to parse simulator errors from transcript", tags.WARNING) + cprint(err, tags.WARNING) + + for line in lines: + words = line.strip().split() + for i, word in enumerate(words): + if not log.has_key("seed") and "seed" in word.lower(): + if words[i+1] == "=": + log["seed"] = words[i+2] + elif words[i+1] != "random": + log["seed"] = words[i+1] + + if not log.has_key("uvm_fatal") and "UVM_FATAL" in word: + if (words[i+1] == ":"): + log["uvm_fatal"] = words[i+2] + + if not log.has_key("uvm_error") and "UVM_ERROR" in word: + if (words[i+1] == ":"): + log["uvm_error"] = words[i+2] + + if "TXN_Total" in word: + if words[i-1] == "[MEM_SCORE]": + log["mem_txns"] = words[i+1] + elif words[i-1] == "[I_CPU_SCORE]": + log["i_cpu_txns"] = words[i+1] + elif words[i-1] == "[D_CPU_SCORE]": + log["d_cpu_txns"] = words[i+1] + elif words[i-1] == "[CPU_SCORE]": + log["cpu_txns"] = words[i+1] + elif words[i-1] == "[MEM_ARB_SCORE]": + log["mem_arb_txns"] = words[i+1] + + if len(log) == len(keys): + break # ignore the rest of the file + + for key in keys: + try: + if key == "uvm_error" or key == "uvm_fatal": + num = int(log[key]) + if (num != 0): + cprint("{key:<15}-> {val}".format(key=key, val=log[key]), tags.FAIL) + else: + cprint("{key:<15}-> {val}".format(key=key, val=log[key]), tags.SUCCESS) + continue + + cprint("{key:<15}-> {val}".format(key=key, val=log[key]), tags.SUCCESS) + + except: + cprint("{key:<15}-> {val}".format(key=key, val="None"), tags.FAIL) + + with open("run_summary.log", "a") as out: + now = datetime.now() + dt_string = now.strftime("%m-%d-%Y %H:%M:%S") + + msg = "[{date}]: ".format(date=dt_string) + + msg += "testcase: {}, ".format(params.testcase) + msg += "seed: {}, ".format(log["seed"]) + msg += "config: {}, ".format(params.config) + msg += "iterations: {}, ".format(params.iterations) + msg += "mem_timeout: {}, ".format(params.mem_timeout) + msg += "mem_latency: {}, ".format(params.mem_latency) + msg += "mmio_latency: {}, ".format(params.mmio_latency) + + for key in keys: + if key == "seed": + continue + try: + msg += "{key}: {val}, ".format(key=key, val=log[key]) + except: + msg += "{key}: None, ".format(key=key) + + out.write(msg) + out.write("\n") diff --git a/verification/uvm/caches/scripts/repeat.py b/verification/uvm/caches/scripts/repeat.py new file mode 100644 index 000000000..96057b054 --- /dev/null +++ b/verification/uvm/caches/scripts/repeat.py @@ -0,0 +1,42 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: repeat.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/17/2022 +# Description: Script for re-running the cache UVM test with the last used parameters +import re + +from cprint import cprint +from cprint import tags + +def repeat(params): + cprint("Repeating Previous Run...", tags.LOG) + try: + with open('run_summary.log', 'r') as f: + last_line = re.sub(r"\[.*\]:", "", f.readlines()[-1]) + except: + cprint("Couldn't open 'run_summary.log' file", tags.FAIL) + cprint("Make sure you have at least one run logged before repeating...", tags.FAIL) + exit() + for param in last_line.split(",")[0:-3]: + pair = param.strip().replace(",", "").split(": ") + if ("txns" not in pair[0]): + setattr(params, pair[0], pair[1]) diff --git a/verification/uvm/caches/scripts/run.do b/verification/uvm/caches/scripts/run.do new file mode 100644 index 000000000..a48285910 --- /dev/null +++ b/verification/uvm/caches/scripts/run.do @@ -0,0 +1,4 @@ +# onerror must be located in .do file +# cannot be inline -do from command line +onerror { quit -f } +run -all \ No newline at end of file diff --git a/verification/uvm/caches/scripts/run.py b/verification/uvm/caches/scripts/run.py new file mode 100644 index 000000000..59f9dbf7c --- /dev/null +++ b/verification/uvm/caches/scripts/run.py @@ -0,0 +1,80 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: run.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Script for running UVM TB for the caches + +import os + +from cprint import cprint +from cprint import tags + +def run(params): + RUN_COMMON = ''' + tb_caches_top -L + {QUESTA_HOME}/uvm-1.2 + -voptargs=+acc + -iterationlimit=100k + -sv_seed {SEED} + +UVM_TESTNAME={TESTCASE}_test + +UVM_VERBOSITY=UVM_{VERBOSITY} + +uvm_set_config_int=*,iterations,{ITERATIONS} + +uvm_set_config_int=*,mem_timeout,{MEM_TIMEOUT} + +uvm_set_config_int=*,mem_latency,{MEM_LATENCY} + +uvm_set_config_int=*,mmio_latency,{MMIO_LATENCY} + -do "coverage save -onexit -p coverage/{TESTCASE}.ucdb" + '''.format( + QUESTA_HOME=os.getenv('QUESTA_HOME'), + SEED=params.seed, + TESTCASE=params.testcase, + VERBOSITY=params.verbosity.upper(), + ITERATIONS=params.iterations, + MEM_TIMEOUT=params.mem_timeout, + MEM_LATENCY=params.mem_latency, + MMIO_LATENCY=params.mmio_latency, + ) + + if (params.gui): + cprint("Running with GUI...", tags.LOG) + res = os.system(''' + vsim -i + {RUN_COMMON} + -do "waves/{WAVE}.do" + -do "scripts/run.do" + '''.format( + RUN_COMMON=RUN_COMMON, + WAVE=params.config + ).replace("\n", " ")) + else: + cprint("Running with Terminal...", tags.LOG) + res = os.system(''' + vsim -c + {RUN_COMMON} + -do "scripts/run.do" + '''.format( + RUN_COMMON=RUN_COMMON, + ).replace("\n", " ")) + + if (res == 0): + cprint("Run Finished", tags.SUCCESS) + else: + cprint("Run Failed", tags.FAIL) \ No newline at end of file diff --git a/verification/uvm/caches/sequences/base_sequence.svh b/verification/uvm/caches/sequences/base_sequence.svh new file mode 100644 index 000000000..4a18fbc7b --- /dev/null +++ b/verification/uvm/caches/sequences/base_sequence.svh @@ -0,0 +1,45 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: base_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/03/2022 +* Description: Base sequence class to abstract N for random sequencing +*/ + +`ifndef BASE_SEQUENCE_SVH +`define BASE_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "cpu_transaction.svh" + +/** Sequence to test read after writes to the same location */ +class base_sequence extends uvm_sequence #(cpu_transaction); + `uvm_object_utils(base_sequence) + function new(string name = ""); + super.new(name); + endfunction : new + + rand int N; // total number of processor side transactions + +endclass : base_sequence +`endif diff --git a/verification/uvm/caches/sequences/evict_sequence.svh b/verification/uvm/caches/sequences/evict_sequence.svh new file mode 100644 index 000000000..2d016be47 --- /dev/null +++ b/verification/uvm/caches/sequences/evict_sequence.svh @@ -0,0 +1,83 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: evict_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence of addresses which map to the same cache frame, forcing a cache eviction +*/ + +`ifndef EVICT_SEQUENCE_SVH +`define EVICT_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "cpu_transaction.svh" +`include "dut_params.svh" +`include "base_sequence.svh" + +class evict_sequence extends base_sequence; + `uvm_object_utils(evict_sequence) + + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + int N_reps; // used to back calculate proper repetitions to used when combined with inner for loop + logic [`L1_INDEX_BITS-1:0] index; + + req_item = cpu_transaction::type_id::create("req_item"); + + N_reps = N / (`L1_ASSOC + 1); + + `uvm_info(this.get_name(), $sformatf("Requested size: %0d; Creating sequence with size N=%0d", + N, N_reps * (`L1_ASSOC + 1)), UVM_LOW) + + if (N_reps <= 0) begin + `uvm_fatal(this.get_name(), + $sformatf( + "Invalid Sequence Size: N must be at least %0d to trigger an eviction event", + (`L1_ASSOC + 1))) + end + + repeat (N_reps) begin + for (int i = 0; i < `L1_ASSOC + 1; i++) begin + start_item(req_item); + if (!req_item.randomize() with { + flush == 0; //TODO: DO WE WANT ANY FLUSH SIGNALS? + if (i != 0) {addr[`L1_INDEX_BITS-1:0] == index;} + rw == '1; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + index = req_item.addr[`L1_INDEX_BITS:0]; + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + end + endtask : body +endclass : evict_sequence + +`endif diff --git a/verification/uvm/caches/sequences/flush_sequence.svh b/verification/uvm/caches/sequences/flush_sequence.svh new file mode 100644 index 000000000..6f1a080f4 --- /dev/null +++ b/verification/uvm/caches/sequences/flush_sequence.svh @@ -0,0 +1,93 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: flush_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/18/2022 +* Description: Sequence that performs randomized flush after read/write +*/ + +`ifndef FLUSH_SEQUENCE_SVH +`define FLUSH_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "cpu_transaction.svh" +`include "base_sequence.svh" + +class flush_sequence extends base_sequence; + `uvm_object_utils(flush_sequence) + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + word_t accesses[word_t]; // queue of previous reads/writes + word_t flushes[word_t]; // queue of previous flushed addresses that need to be checked + + req_item = cpu_transaction::type_id::create("req_item"); + + `uvm_info(this.get_name(), $sformatf("Creating sequence with size N=%0d", N), UVM_LOW) + + repeat (N) begin + start_item(req_item); + + if (!req_item.randomize() with { + flush dist { + 1 := 25, + 0 := 75 + }; //force a 25%/75% distribution of flushes + rw dist { + 1 := 50, + 0 := 50 + }; //force a 50%/50% distribution of reads/writes + + if (flush == 1) { + //flush from previously accessed addr + addr inside {accesses}; + } else + if (flushes.size() > 0) {addr inside {flushes};} + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (req_item.flush == 0) begin + accesses[req_item.addr] = req_item.addr; + + if (flushes.exists(req_item.addr)) begin + flushes.delete(req_item.addr); + end + end else begin + // flush + accesses.delete(req_item.addr); + flushes[req_item.addr] = req_item.addr; + end + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + endtask : body +endclass : flush_sequence + +`endif diff --git a/verification/uvm/caches/sequences/index_sequence.svh b/verification/uvm/caches/sequences/index_sequence.svh new file mode 100644 index 000000000..282778af7 --- /dev/null +++ b/verification/uvm/caches/sequences/index_sequence.svh @@ -0,0 +1,98 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: index_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence that reads/writes to the same cache block to ensure proper word indexing inside a block +*/ + +`ifndef INDEX_SEQUENCE_SVH +`define INDEX_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "cpu_transaction.svh" +`include "dut_params.svh" +`include "base_sequence.svh" + +class index_sequence extends base_sequence; + `uvm_object_utils(index_sequence) + + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + int N_reps; // used to back calculate proper repetitions to used when combined with inner for loop + + logic [`L1_ADDR_IDX_SIZE-1:0] index; + word_t block_words[word_t]; + + req_item = cpu_transaction::type_id::create("req_item"); + + N_reps = N / (`L1_BLOCK_SIZE); + + `uvm_info(this.get_name(), $sformatf("Requested size: %0d; Creating sequence with size N=%0d", + N, N_reps * (`L1_BLOCK_SIZE)), UVM_LOW) + + if (N_reps <= 0) begin + `uvm_fatal(this.get_name(), + $sformatf( + "Invalid Sequence Size: N must be at least %0d to touch all words of a block", + (`L1_BLOCK_SIZE))) + end + + repeat (N_reps) begin + for (int i = 0; i < `L1_BLOCK_SIZE; i++) begin + start_item(req_item); + if (!req_item.randomize() with { + flush == 0; //TODO: DO WE WANT ANY FLUSH SIGNALS? + if (i != 0) { + // first iteration is completely random txn + addr[31:`L1_ADDR_IDX_END] == index; + addr inside {block_words}; + } + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + index = req_item.addr[31:`L1_ADDR_IDX_END]; + + if (i == 0) begin + // initialize block words arrays + for (int j = 0; j < `L1_BLOCK_SIZE * 4; j += 4) begin + word_t temp = {index, j[`L1_ADDR_IDX_END-1:0]}; + block_words[temp] = temp; + end + end + + block_words.delete(req_item.addr); // remove from list of addresses to r/w + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + end + endtask : body +endclass : index_sequence + +`endif diff --git a/verification/uvm/caches/sequences/master_sequence.svh b/verification/uvm/caches/sequences/master_sequence.svh new file mode 100644 index 000000000..23fde3d28 --- /dev/null +++ b/verification/uvm/caches/sequences/master_sequence.svh @@ -0,0 +1,174 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: master_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence that randomly interleaves all other sequences +*/ + +`ifndef MASTER_SEQUENCE_SVH +`define MASTER_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "dut_params.svh" + +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "flush_sequence.svh" +`include "base_sequence.svh" + +`include "cpu_transaction.svh" + + +class bounds; + rand int upper; + int lower; + + constraint ub {upper > lower;} + + function new(int _lower); + lower = _lower; + endfunction : new + + function string sprint(); + return $sformatf("(%0d, %0d)", lower, upper); + endfunction : sprint +endclass : bounds + +class sub_master_sequence; + rand bounds nom_bounds; + rand bounds evt_bounds; + rand bounds idx_bounds; + rand bounds mmio_bounds; + rand bounds flush_bounds; + + constraint nom {nom_bounds.upper < 10;} + constraint evt {evt_bounds.upper < 10;} + constraint idx {idx_bounds.upper < 10;} + constraint mmio {mmio_bounds.upper < 10;} + constraint flush {flush_bounds.upper < 10;} + + function new(); + nom_bounds = new(1); + evt_bounds = new(`L1_ASSOC + 1); + idx_bounds = new(`L1_BLOCK_SIZE); + mmio_bounds = new(1); + flush_bounds = new(1); + endfunction : new + + function void show(); + `uvm_info( + "sub_master_seq", + $sformatf( + "evt_bounds: %s, idx_bounds: %s, nom_bounds: %s, mmio_bounds: %s, flush_bounds: %s", + evt_bounds.sprint(), idx_bounds.sprint(), nom_bounds.sprint(), mmio_bounds.sprint(), + flush_bounds.sprint()), UVM_LOW); + endfunction +endclass : sub_master_sequence + +class master_sequence extends base_sequence; + `uvm_object_utils(master_sequence) + `uvm_declare_p_sequencer(cpu_sequencer) + + sub_master_sequence seq_param; + + nominal_sequence nom_seq; + index_sequence idx_seq; + evict_sequence evt_seq; + mmio_sequence mmio_seq; + flush_sequence flush_seq; + + function new(string name = ""); + super.new(name); + nom_seq = nominal_sequence::type_id::create("nom_seq"); + idx_seq = index_sequence::type_id::create("idx_seq"); + evt_seq = evict_sequence::type_id::create("evt_seq"); + mmio_seq = mmio_sequence::type_id::create("mmio_seq"); + flush_seq = flush_sequence::type_id::create("flush_seq"); + seq_param = new(); + endfunction : new + + function void sub_randomize(); + //randomize sub-sequences + if (!nom_seq.randomize() with { + N inside {[seq_param.nom_bounds.lower : seq_param.nom_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (!idx_seq.randomize() with { + N inside {[seq_param.idx_bounds.lower : seq_param.idx_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (!evt_seq.randomize() with { + N inside {[seq_param.evt_bounds.lower : seq_param.evt_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (!mmio_seq.randomize() with { + N inside {[seq_param.mmio_bounds.lower : seq_param.mmio_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (!flush_seq.randomize() with { + N inside {[seq_param.flush_bounds.lower : seq_param.flush_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + endfunction + + task body(); + cpu_transaction req_item; + base_sequence seq_list [$]; + seq_list.push_back(nom_seq); + seq_list.push_back(idx_seq); + seq_list.push_back(evt_seq); + seq_list.push_back(mmio_seq); + seq_list.push_back(flush_seq); + + `uvm_info(this.get_name(), $sformatf("running %0d iterations", N), UVM_LOW) + + while (N > 0) begin + if (!seq_param.randomize()) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + seq_param.show(); // display sequence parameters + sub_randomize(); // randomize sub sequences + + seq_list.shuffle(); // reorder list elements to get random ordering + + for (int i = 0; i < seq_list.size(); i++) begin + seq_list[i].start(p_sequencer); + N -= seq_list[i].N; + end + end + endtask : body +endclass : master_sequence + +`endif diff --git a/verification/uvm/caches/sequences/mmio_sequence.svh b/verification/uvm/caches/sequences/mmio_sequence.svh new file mode 100644 index 000000000..6c9d19435 --- /dev/null +++ b/verification/uvm/caches/sequences/mmio_sequence.svh @@ -0,0 +1,66 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mmio_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence that reads/writes to memory mapped I/O address space +*/ + +`ifndef MMIO_SEQUENCE_SVH +`define MMIO_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "cpu_transaction.svh" +`include "dut_params.svh" + +class mmio_sequence extends base_sequence; + `uvm_object_utils(mmio_sequence) + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + + req_item = cpu_transaction::type_id::create("req_item"); + + `uvm_info(this.get_name(), $sformatf("Creating sequence with size N=%0d", N), UVM_LOW) + + repeat (N) begin + start_item(req_item); + + if (!req_item.randomize() with { + flush == 0; //TODO: DO WE WANT ANY FLUSH SIGNALS? + addr >= `NONCACHE_START_ADDR; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + endtask : body +endclass : mmio_sequence + +`endif diff --git a/verification/uvm/caches/sequences/nominal_sequence.svh b/verification/uvm/caches/sequences/nominal_sequence.svh new file mode 100644 index 000000000..f110aca14 --- /dev/null +++ b/verification/uvm/caches/sequences/nominal_sequence.svh @@ -0,0 +1,91 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: nominal_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence that performs randomized read after writes +*/ + +`ifndef NOMINAL_SEQUENCE_SVH +`define NOMINAL_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "cpu_transaction.svh" +`include "base_sequence.svh" + +class nominal_sequence extends base_sequence; + `uvm_object_utils(nominal_sequence) + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + int write_count; // current number of writes + word_t writes[word_t]; // queue of write addresses + + req_item = cpu_transaction::type_id::create("req_item"); + + write_count = 0; + + `uvm_info(this.get_name(), $sformatf("Creating sequence with size N=%0d", N), UVM_LOW) + + repeat (N) begin + start_item(req_item); + + if (!req_item.randomize() with { + flush == 0; //TODO: DO WE WANT ANY FLUSH SIGNALS IN NOMINAL OPPERATION? + rw dist { + 1 := 1, + 0 := 1 + }; //force a 50/50 distribution of reads/writes + if (write_count > N / 2) { + //only reads allowed + rw == 0; + } + if (rw == 0) { + //read from previously written addr + addr inside {writes}; + } + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (req_item.rw) begin + // write + write_count++; + writes[req_item.addr] = req_item.addr; + end else begin + // read + writes.delete(req_item.addr); + end + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + endtask : body +endclass : nominal_sequence + +`endif diff --git a/verification/uvm/caches/tb_caches_top.sv b/verification/uvm/caches/tb_caches_top.sv new file mode 100644 index 000000000..e04b210f8 --- /dev/null +++ b/verification/uvm/caches/tb_caches_top.sv @@ -0,0 +1,217 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_caches_top.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Top Level Module for UVM Cache Verification +*/ + +// package file +`include "rv32i_types_pkg.sv" + +// design file +`include "l1_cache.sv" +// `include "l2_cache.sv" +`include "memory_arbiter.sv" + +// Interface checker file +`include "interface_checker.svh" + +// interface file +`include "generic_bus_if.vh" +`include "cache_if.svh" + +// UVM test file +`include "nominal_test.svh" +`include "index_test.svh" +`include "evict_test.svh" +`include "mmio_test.svh" +`include "flush_test.svh" +`include "random_test.svh" + +// Device Parameter Build Constants +`include "dut_params.svh" + +`timescale 1ns / 1ps +// import uvm packages +import uvm_pkg::*; + +module tb_caches_top (); + logic clk; + + // generate clock + initial begin + clk = 0; + forever #(`CLK_PERIOD) clk = !clk; + end + + // instantiate the interface + generic_bus_if i_cpu_bus_if (); // from processor to instruction l1 cache + generic_bus_if d_cpu_bus_if (); // from processor to data l1 cache + generic_bus_if i_l1_arb_bus_if (); // from instruction l1 cache to memory arbiter + generic_bus_if d_l1_arb_bus_if (); // from data l1 cache to memory arbiter + generic_bus_if arb_l2_bus_if (); // from memory arbiter to l2 cache + generic_bus_if mem_bus_if (); // from l2 cache to memory bus + + cache_if i_cif (clk); // holds flush, clear signals for i cache + cache_if d_cif (clk); // holds flush, clear signals for d cache + cache_if l2_cif (clk); // holds flush, clear signals for l2 cache + + if (`INTERFACE_CHECKER == 1) begin + interface_checker if_check ( //FIXME: THIS NEEDS TO BE UPDATED WITH PROPER INTERFACES + .d_cif(d_cif.cache), + .i_cif(i_cif.cache), + .l2_cif(l2_cif.cache), + .d_cpu_if(d_cpu_bus_if.generic_bus), + .i_cpu_if(i_cpu_bus_if.generic_bus), + .d_l1_arb_bus_if(d_l1_arb_bus_if.generic_bus), + .i_l1_arb_bus_if(i_l1_arb_bus_if.generic_bus), + .arb_l2_bus_if(arb_l2_bus_if.generic_bus), + .mem_if(mem_bus_if.generic_bus) + ); + end + + + /********************** Instantiate the DUT **********************/ + + +`ifdef TB_L1_CONFIG + // L1 Cache + l1_cache #( + .CACHE_SIZE(`L1_CACHE_SIZE), + .BLOCK_SIZE(`L1_BLOCK_SIZE), + .ASSOC(`L1_ASSOC), + .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + ) l1 ( + .CLK(d_cif.CLK), + .nRST(d_cif.nRST), + .clear(d_cif.clear), + .flush(d_cif.flush), + .clear_done(d_cif.clear_done), + .flush_done(d_cif.flush_done), + .proc_gen_bus_if(d_cpu_bus_if.generic_bus), + .mem_gen_bus_if(mem_bus_if.cpu) + ); +`endif + +`ifdef TB_L2_CONFIG + // L2 + // l2_cache #( + // .CACHE_SIZE(`L2_CACHE_SIZE), + // .BLOCK_SIZE(`L2_BLOCK_SIZE), + // .ASSOC(`L2_ASSOC), + // .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + // ) l2 ( + // .CLK(l2_cif.CLK), + // .nRST(l2_cif.nRST), + // .clear(l2_cif.clear), + // .flush(l2_cif.flush), + // .clear_done(l2_cif.clear_done), + // .flush_done(l2_cif.flush_done), + // .proc_gen_bus_if(arb_l2_bus_if.generic_bus), + // .mem_gen_bus_if(mem_bus_if.cpu) + // ); +`endif + +`ifdef TB_FULL_CONFIG + // Data L1 + l1_cache #( + .CACHE_SIZE(`L1_CACHE_SIZE), + .BLOCK_SIZE(`L1_BLOCK_SIZE), + .ASSOC(`L1_ASSOC), + .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + ) d_l1 ( + .CLK(d_cif.CLK), + .nRST(d_cif.nRST), + .clear(d_cif.clear), + .flush(d_cif.flush), + .clear_done(d_cif.clear_done), + .flush_done(d_cif.flush_done), + .proc_gen_bus_if(d_cpu_bus_if.generic_bus), + .mem_gen_bus_if(d_l1_arb_bus_if.cpu) + ); + + assign i_cif.nRST = d_cif.nRST; + + // Instruction L1 + l1_cache #( + .CACHE_SIZE(`L1_CACHE_SIZE), + .BLOCK_SIZE(`L1_BLOCK_SIZE), + .ASSOC(`L1_ASSOC), + .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + ) i_l1 ( + .CLK(i_cif.CLK), + .nRST(i_cif.nRST), + .clear(i_cif.clear), + .flush(i_cif.flush), + .clear_done(i_cif.clear_done), + .flush_done(i_cif.flush_done), + .proc_gen_bus_if(i_cpu_bus_if.generic_bus), + .mem_gen_bus_if(i_l1_arb_bus_if.cpu) + ); + + // Memory Arbiter + memory_arbiter mem_arb ( + .CLK(d_cif.CLK), + .nRST(d_cif.nRST), + .icache_if(i_l1_arb_bus_if.generic_bus), + .dcache_if(d_l1_arb_bus_if.generic_bus), + .mem_arb_if(arb_l2_bus_if.cpu) + ); + + assign l2_cif.nRST = d_cif.nRST; + assign l2_cif.flush = d_cif.flush; + assign l2_cif.clear = d_cif.clear; + + // L2 + // l2_cache #( + // .CACHE_SIZE(`L2_CACHE_SIZE), + // .BLOCK_SIZE(`L2_BLOCK_SIZE), + // .ASSOC(`L2_ASSOC), + // .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + // ) l2 ( + // .CLK(l2_cif.CLK), + // .nRST(l2_cif.nRST), + // .clear(l2_cif.clear), + // .flush(l2_cif.flush), + // .clear_done(l2_cif.clear_done), + // .flush_done(l2_cif.flush_done), + // .proc_gen_bus_if(arb_l2_bus_if.generic_bus), + // .mem_gen_bus_if(mem_bus_if.cpu) + // ); +`endif + + initial begin + uvm_config_db#(virtual cache_if)::set(null, "", "i_cif", i_cif); + uvm_config_db#(virtual cache_if)::set(null, "", "d_cif", d_cif); + uvm_config_db#(virtual cache_if)::set(null, "", "l2_cif", l2_cif); + + uvm_config_db#(virtual generic_bus_if)::set(null, "", "i_cpu_bus_if", i_cpu_bus_if); + uvm_config_db#(virtual generic_bus_if)::set(null, "", "d_cpu_bus_if", d_cpu_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(null, "", "i_l1_arb_bus_if", i_l1_arb_bus_if); + uvm_config_db#(virtual generic_bus_if)::set(null, "", "d_l1_arb_bus_if", d_l1_arb_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(null, "", "arb_l2_bus_if", arb_l2_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(null, "", "mem_bus_if", mem_bus_if); + + run_test(); + end +endmodule diff --git a/verification/uvm/caches/testplan/testplan.ods b/verification/uvm/caches/testplan/testplan.ods new file mode 100644 index 0000000000000000000000000000000000000000..020f0a3872dffea382e2377e2cfab407f639ac60 GIT binary patch literal 14739 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 literal 0 HcmV?d00001 diff --git a/verification/uvm/caches/tests/base_test.svh b/verification/uvm/caches/tests/base_test.svh new file mode 100644 index 000000000..0d180f13f --- /dev/null +++ b/verification/uvm/caches/tests/base_test.svh @@ -0,0 +1,174 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: base_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test with default settings/configurations +*/ + +`ifndef BASE_TEST_SVH +`define BASE_TEST_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "cache_env.svh" +`include "cache_env_config.svh" + +`include "generic_bus_if.vh" +`include "cache_if.svh" + +class base_test #( + type sequence_type = nominal_sequence, + string sequence_name = "BASE_TEST" +) extends uvm_test; + `uvm_component_utils(base_test) + + sequence_type seq; + + cache_env_config env_config; + cache_env env; + virtual cache_if d_cif; + virtual cache_if i_cif; + virtual cache_if l2_cif; + + virtual generic_bus_if d_cpu_bus_if; + virtual generic_bus_if i_cpu_bus_if; + + virtual generic_bus_if d_l1_arb_bus_if; + virtual generic_bus_if i_l1_arb_bus_if; + + virtual generic_bus_if arb_l2_bus_if; + virtual generic_bus_if mem_bus_if; + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + env_config = cache_env_config::type_id::create("ENV_CONFIG", this); + if (!env_config.randomize()) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + env = cache_env::type_id::create("ENV", this); + env.env_config = env_config; + + seq = sequence_type::type_id::create(sequence_name); + + // get interfaces from db + if (!uvm_config_db#(virtual cache_if)::get(this, "", "d_cif", d_cif)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/d_cif", "No virtual interface specified for this test instance") + end + if (!uvm_config_db#(virtual cache_if)::get(this, "", "i_cif", i_cif)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/i_cif", "No virtual interface specified for this test instance") + end + if (!uvm_config_db#(virtual cache_if)::get(this, "", "l2_cif", l2_cif)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/l2_cif", "No virtual interface specified for this test instance") + end + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", "d_cpu_bus_if", d_cpu_bus_if)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/d_cpu_bus_if", "No virtual interface specified for this test instance") + end + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", "i_cpu_bus_if", i_cpu_bus_if)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/i_cpu_bus_if", "No virtual interface specified for this test instance") + end + + if (!uvm_config_db#(virtual generic_bus_if)::get( + this, "", "d_l1_arb_bus_if", d_l1_arb_bus_if + )) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/d_l1_arb_bus_if", "No virtual interface specified for this test instance") + end + if (!uvm_config_db#(virtual generic_bus_if)::get( + this, "", "i_l1_arb_bus_if", i_l1_arb_bus_if + )) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/i_l1_arb_bus_if", "No virtual interface specified for this test instance") + end + + if (!uvm_config_db#(virtual generic_bus_if)::get( + this, "", "arb_l2_bus_if", arb_l2_bus_if + )) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/arb_l2_bus_if", "No virtual interface specified for this test instance") + end + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", "mem_bus_if", mem_bus_if)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/mem_bus_if", "No virtual interface specified for this test instance") + end + + // send the interfaces down + //TODO: SHOULD I NARROW THE SCOPE OF THE ENV_CONFIG? + uvm_config_db#(cache_env_config)::set(this, "*", "env_config", env_config); + + uvm_config_db#(virtual cache_if)::set(this, "env.agt*", "i_cif", i_cif); + uvm_config_db#(virtual cache_if)::set(this, "env.agt*", "d_cif", d_cif); + uvm_config_db#(virtual cache_if)::set(this, "env.agt*", "l2_cif", l2_cif); + + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "d_cpu_bus_if", d_cpu_bus_if); + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "i_cpu_bus_if", i_cpu_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "d_l1_arb_bus_if", + d_l1_arb_bus_if); + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "i_l1_arb_bus_if", + i_l1_arb_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "arb_l2_bus_if", arb_l2_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "mem_bus_if", mem_bus_if); + endfunction : build_phase + + task run_phase(uvm_phase phase); + phase.raise_objection(this, $sformatf("Starting <%s> in main phase", sequence_name)); + if (!seq.randomize() with { + if (env_config.iterations > 0) { + N == env_config.iterations; //command line request for iterations + } else { + N inside {[20 : 100]}; //default number of memory accesses + } + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + +`ifdef TB_L1_CONFIG + seq.start(env.cpu_agt.sqr); +`endif + +`ifdef TB_L2_CONFIG + seq.start(env.mem_arb_agt.sqr); +`endif + +`ifdef TB_FULL_CONFIG + seq.start(env.d_cpu_agt.sqr); +`endif + #5ns; + phase.drop_objection(this, $sformatf("Finished <%s> in main phase", sequence_name)); + endtask + +endclass : base_test + +`endif diff --git a/verification/uvm/caches/tests/evict_test.svh b/verification/uvm/caches/tests/evict_test.svh new file mode 100644 index 000000000..067f7e514 --- /dev/null +++ b/verification/uvm/caches/tests/evict_test.svh @@ -0,0 +1,41 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: evict_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for eviction_sequence +*/ + +`ifndef EVICT_TEST_SVH +`define EVICT_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "uvm_macros.svh" + +class evict_test extends base_test #(evict_sequence, "EVICT_SEQ"); + `uvm_component_utils(evict_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : evict_test + +`endif diff --git a/verification/uvm/caches/tests/flush_test.svh b/verification/uvm/caches/tests/flush_test.svh new file mode 100644 index 000000000..9473ffe02 --- /dev/null +++ b/verification/uvm/caches/tests/flush_test.svh @@ -0,0 +1,45 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: flush_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for nominal_test +*/ + +`ifndef FLUSH_TEST_SVH +`define FLUSH_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "flush_sequence.svh" +`include "uvm_macros.svh" + + +class flush_test extends base_test #(flush_sequence, "FLUSH_SEQ"); + `uvm_component_utils(flush_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : flush_test + +`endif + + diff --git a/verification/uvm/caches/tests/index_test.svh b/verification/uvm/caches/tests/index_test.svh new file mode 100644 index 000000000..646b25068 --- /dev/null +++ b/verification/uvm/caches/tests/index_test.svh @@ -0,0 +1,42 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: index_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for index_sequence +*/ + +`ifndef INDEX_TEST_SVH +`define INDEX_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "uvm_macros.svh" +`include "index_sequence.svh" + +class index_test extends base_test #(index_sequence, "INDEX_SEQ"); + `uvm_component_utils(index_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : index_test + +`endif diff --git a/verification/uvm/caches/tests/mmio_test.svh b/verification/uvm/caches/tests/mmio_test.svh new file mode 100644 index 000000000..c78b1167a --- /dev/null +++ b/verification/uvm/caches/tests/mmio_test.svh @@ -0,0 +1,45 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mmio_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for mmio_sequence +*/ + +`ifndef MMIO_TEST_SVH +`define MMIO_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "mmio_sequence.svh" +`include "uvm_macros.svh" + + +class mmio_test extends base_test #(mmio_sequence, "MMIO_SEQ"); + `uvm_component_utils(mmio_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : mmio_test + +`endif + + diff --git a/verification/uvm/caches/tests/nominal_test.svh b/verification/uvm/caches/tests/nominal_test.svh new file mode 100644 index 000000000..0e5d348f2 --- /dev/null +++ b/verification/uvm/caches/tests/nominal_test.svh @@ -0,0 +1,47 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: nominal_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for nominal_test +*/ + +`ifndef NOMINAL_TEST_SVH +`define NOMINAL_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "nominal_sequence.svh" +`include "uvm_macros.svh" + +//TODO: TRY TO FIGURE OUT HOW TO MAKE THIS TYPEDEF WORK +// typedef base_test#(nominal_sequence, "NOMINAL_SEQ") nominal_test; + +class nominal_test extends base_test #(nominal_sequence, "NOMINAL_SEQ"); + `uvm_component_utils(nominal_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : nominal_test + +`endif + + diff --git a/verification/uvm/caches/tests/random_test.svh b/verification/uvm/caches/tests/random_test.svh new file mode 100644 index 000000000..baf038708 --- /dev/null +++ b/verification/uvm/caches/tests/random_test.svh @@ -0,0 +1,44 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: index_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for master_sequence +*/ + +`ifndef RANDOM_TEST_SVH +`define RANDOM_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "master_sequence.svh" +`include "uvm_macros.svh" + +class random_test extends base_test #(master_sequence, "RANDOM_TEST"); + `uvm_component_utils(random_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : random_test + +`endif + + diff --git a/verification/uvm/caches/waves/full.do b/verification/uvm/caches/waves/full.do new file mode 100644 index 000000000..f8768697d --- /dev/null +++ b/verification/uvm/caches/waves/full.do @@ -0,0 +1,206 @@ +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate /tb_caches_top/i_cif/CLK +add wave -noupdate -color Salmon /tb_caches_top/i_cif/nRST +add wave -noupdate -group i_cpu_bus_if /tb_caches_top/i_cpu_bus_if/addr +add wave -noupdate -group i_cpu_bus_if -color Salmon /tb_caches_top/i_cpu_bus_if/wdata +add wave -noupdate -group i_cpu_bus_if /tb_caches_top/i_cpu_bus_if/rdata +add wave -noupdate -group i_cpu_bus_if -color Cyan /tb_caches_top/i_cpu_bus_if/ren +add wave -noupdate -group i_cpu_bus_if -color Violet /tb_caches_top/i_cpu_bus_if/wen +add wave -noupdate -group i_cpu_bus_if -color Orange /tb_caches_top/i_cpu_bus_if/busy +add wave -noupdate -group i_cpu_bus_if /tb_caches_top/i_cpu_bus_if/byte_en +add wave -noupdate -expand -group d_cpu_bus_if /tb_caches_top/d_cpu_bus_if/addr +add wave -noupdate -expand -group d_cpu_bus_if -color Salmon /tb_caches_top/d_cpu_bus_if/wdata +add wave -noupdate -expand -group d_cpu_bus_if /tb_caches_top/d_cpu_bus_if/rdata +add wave -noupdate -expand -group d_cpu_bus_if -color Cyan /tb_caches_top/d_cpu_bus_if/ren +add wave -noupdate -expand -group d_cpu_bus_if -color Violet /tb_caches_top/d_cpu_bus_if/wen +add wave -noupdate -expand -group d_cpu_bus_if -color Orange /tb_caches_top/d_cpu_bus_if/busy +add wave -noupdate -expand -group d_cpu_bus_if /tb_caches_top/d_cpu_bus_if/byte_en +add wave -noupdate -group i_l1_arb_bus_if /tb_caches_top/i_l1_arb_bus_if/addr +add wave -noupdate -group i_l1_arb_bus_if -color Salmon /tb_caches_top/i_l1_arb_bus_if/wdata +add wave -noupdate -group i_l1_arb_bus_if /tb_caches_top/i_l1_arb_bus_if/rdata +add wave -noupdate -group i_l1_arb_bus_if -color Cyan /tb_caches_top/i_l1_arb_bus_if/ren +add wave -noupdate -group i_l1_arb_bus_if -color Violet /tb_caches_top/i_l1_arb_bus_if/wen +add wave -noupdate -group i_l1_arb_bus_if -color Orange /tb_caches_top/i_l1_arb_bus_if/busy +add wave -noupdate -group i_l1_arb_bus_if /tb_caches_top/i_l1_arb_bus_if/byte_en +add wave -noupdate -expand -group d_l1_arb_bus_if /tb_caches_top/d_l1_arb_bus_if/addr +add wave -noupdate -expand -group d_l1_arb_bus_if -color Salmon /tb_caches_top/d_l1_arb_bus_if/wdata +add wave -noupdate -expand -group d_l1_arb_bus_if /tb_caches_top/d_l1_arb_bus_if/rdata +add wave -noupdate -expand -group d_l1_arb_bus_if -color Cyan /tb_caches_top/d_l1_arb_bus_if/ren +add wave -noupdate -expand -group d_l1_arb_bus_if -color Violet /tb_caches_top/d_l1_arb_bus_if/wen +add wave -noupdate -expand -group d_l1_arb_bus_if -color Orange /tb_caches_top/d_l1_arb_bus_if/busy +add wave -noupdate -expand -group d_l1_arb_bus_if /tb_caches_top/d_l1_arb_bus_if/byte_en +add wave -noupdate -expand -group arb_l2_bus_if /tb_caches_top/arb_l2_bus_if/addr +add wave -noupdate -expand -group arb_l2_bus_if -color Salmon /tb_caches_top/arb_l2_bus_if/wdata +add wave -noupdate -expand -group arb_l2_bus_if /tb_caches_top/arb_l2_bus_if/rdata +add wave -noupdate -expand -group arb_l2_bus_if -color Cyan /tb_caches_top/arb_l2_bus_if/ren +add wave -noupdate -expand -group arb_l2_bus_if -color Violet /tb_caches_top/arb_l2_bus_if/wen +add wave -noupdate -expand -group arb_l2_bus_if -color Orange /tb_caches_top/arb_l2_bus_if/busy +add wave -noupdate -expand -group arb_l2_bus_if /tb_caches_top/arb_l2_bus_if/byte_en +add wave -noupdate -expand -group mem_bus_if /tb_caches_top/mem_bus_if/addr +add wave -noupdate -expand -group mem_bus_if -color Salmon /tb_caches_top/mem_bus_if/wdata +add wave -noupdate -expand -group mem_bus_if /tb_caches_top/mem_bus_if/rdata +add wave -noupdate -expand -group mem_bus_if -color Cyan /tb_caches_top/mem_bus_if/ren +add wave -noupdate -expand -group mem_bus_if -color Violet /tb_caches_top/mem_bus_if/wen +add wave -noupdate -expand -group mem_bus_if -color Orange /tb_caches_top/mem_bus_if/busy +add wave -noupdate -expand -group mem_bus_if /tb_caches_top/mem_bus_if/byte_en +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/CACHE_SIZE +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/BLOCK_SIZE +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/ASSOC +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/NONCACHE_START_ADDR +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_TOTAL_FRAMES +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_SETS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_FRAME_BITS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_SET_BITS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_BLOCK_BITS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_TAG_BITS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/FRAME_SIZE +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/CLK +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/nRST +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/clear +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/flush +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/clear_done +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/flush_done +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/set_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_set_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/en_set_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/clr_set_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/frame_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_frame_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/en_frame_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/clr_frame_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/word_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_word_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/en_word_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/clr_word_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/finish_word +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/finish_frame +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/finish_set +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/state +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_state +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/cache +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_cache +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/ridx +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/last_used +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_last_used +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/read_addr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_read_addr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/decoded_addr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/hit +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/pass_through +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/hit_data +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/hit_idx +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/CACHE_SIZE +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/BLOCK_SIZE +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/ASSOC +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/NONCACHE_START_ADDR +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_TOTAL_FRAMES +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_SETS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_FRAME_BITS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_SET_BITS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_BLOCK_BITS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_TAG_BITS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/FRAME_SIZE +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/CLK +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/nRST +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/clear +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/flush +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/clear_done +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/flush_done +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/set_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_set_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/en_set_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/clr_set_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/frame_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_frame_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/en_frame_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/clr_frame_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/word_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_word_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/en_word_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/clr_word_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/finish_word +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/finish_frame +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/finish_set +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/state +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_state +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/cache +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_cache +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/ridx +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/last_used +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_last_used +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/read_addr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_read_addr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/decoded_addr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/hit +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/pass_through +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/hit_data +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/hit_idx +add wave -noupdate -group mem_arb /tb_caches_top/mem_arb/CLK +add wave -noupdate -group mem_arb /tb_caches_top/mem_arb/nRST +add wave -noupdate -group mem_arb /tb_caches_top/mem_arb/state +add wave -noupdate -group mem_arb /tb_caches_top/mem_arb/next_state +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/CACHE_SIZE +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/BLOCK_SIZE +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/ASSOC +add wave -noupdate -group l2 -group params /tb_caches_top/l2/NONCACHE_START_ADDR +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TOTAL_FRAMES +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_SETS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_FRAME_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_SET_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_BLOCK_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TAG_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/FRAME_SIZE +add wave -noupdate -group l2 -group if /tb_caches_top/l2/CLK +add wave -noupdate -group l2 -group if /tb_caches_top/l2/nRST +add wave -noupdate -group l2 -group if /tb_caches_top/l2/clear +add wave -noupdate -group l2 -group if /tb_caches_top/l2/flush +add wave -noupdate -group l2 -group if /tb_caches_top/l2/clear_done +add wave -noupdate -group l2 -group if /tb_caches_top/l2/flush_done +add wave -noupdate -group l2 /tb_caches_top/l2/set_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_set_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_set_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_set_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/frame_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_frame_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_frame_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_frame_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/word_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_word_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_word_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_word_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/finish_word +add wave -noupdate -group l2 /tb_caches_top/l2/finish_frame +add wave -noupdate -group l2 /tb_caches_top/l2/finish_set +add wave -noupdate -group l2 /tb_caches_top/l2/state +add wave -noupdate -group l2 /tb_caches_top/l2/next_state +add wave -noupdate -group l2 /tb_caches_top/l2/cache +add wave -noupdate -group l2 /tb_caches_top/l2/next_cache +add wave -noupdate -group l2 /tb_caches_top/l2/decoded_addr +add wave -noupdate -group l2 /tb_caches_top/l2/hit +add wave -noupdate -group l2 /tb_caches_top/l2/pass_through +add wave -noupdate -group l2 /tb_caches_top/l2/hit_data +add wave -noupdate -group l2 /tb_caches_top/l2/hit_idx +add wave -noupdate -group l2 /tb_caches_top/l2/lru +add wave -noupdate -group l2 /tb_caches_top/l2/nextlru +add wave -noupdate -group l2 /tb_caches_top/l2/ridx +add wave -noupdate -group l2 /tb_caches_top/l2/read_addr +add wave -noupdate -group l2 /tb_caches_top/l2/next_read_addr +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {198037 ps} 0} +quietly wave cursor active 1 +configure wave -namecolwidth 150 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 1 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ps +update +WaveRestoreZoom {14943475 ps} {15163975 ps} diff --git a/verification/uvm/caches/waves/l1.do b/verification/uvm/caches/waves/l1.do new file mode 100644 index 000000000..2e4df743a --- /dev/null +++ b/verification/uvm/caches/waves/l1.do @@ -0,0 +1,88 @@ +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate /tb_caches_top/d_cif/CLK +add wave -noupdate -color Firebrick /tb_caches_top/d_cif/nRST +add wave -noupdate -expand -group cif /tb_caches_top/d_cif/clear +add wave -noupdate -expand -group cif -color {Medium Spring Green} /tb_caches_top/d_cif/flush +add wave -noupdate -expand -group cif /tb_caches_top/d_cif/clear_done +add wave -noupdate -expand -group cif -color {Medium Spring Green} /tb_caches_top/d_cif/flush_done +add wave -noupdate -expand -group cpu_bus_if -color Magenta /tb_caches_top/d_cpu_bus_if/addr +add wave -noupdate -expand -group cpu_bus_if -color Cyan /tb_caches_top/d_cpu_bus_if/wdata +add wave -noupdate -expand -group cpu_bus_if /tb_caches_top/d_cpu_bus_if/rdata +add wave -noupdate -expand -group cpu_bus_if -color {Indian Red} /tb_caches_top/d_cpu_bus_if/ren +add wave -noupdate -expand -group cpu_bus_if -color {Green Yellow} /tb_caches_top/d_cpu_bus_if/wen +add wave -noupdate -expand -group cpu_bus_if -color Red /tb_caches_top/d_cpu_bus_if/busy +add wave -noupdate -expand -group cpu_bus_if -color Gold /tb_caches_top/d_cpu_bus_if/byte_en +add wave -noupdate -expand -group mem_bus_if -color Magenta /tb_caches_top/mem_bus_if/addr +add wave -noupdate -expand -group mem_bus_if -color Cyan /tb_caches_top/mem_bus_if/wdata +add wave -noupdate -expand -group mem_bus_if /tb_caches_top/mem_bus_if/rdata +add wave -noupdate -expand -group mem_bus_if -color {Indian Red} /tb_caches_top/mem_bus_if/ren +add wave -noupdate -expand -group mem_bus_if -color {Green Yellow} /tb_caches_top/mem_bus_if/wen +add wave -noupdate -expand -group mem_bus_if -color Red /tb_caches_top/mem_bus_if/busy +add wave -noupdate -expand -group mem_bus_if -color Gold /tb_caches_top/mem_bus_if/byte_en +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/CACHE_SIZE +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/BLOCK_SIZE +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/ASSOC +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/NONCACHE_START_ADDR +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_TOTAL_FRAMES +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_SETS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_FRAME_BITS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_SET_BITS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_BLOCK_BITS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_TAG_BITS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/FRAME_SIZE +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/CLK +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/nRST +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/clear +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/flush +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/clear_done +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/flush_done +add wave -noupdate -expand -group l1 /tb_caches_top/l1/set_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_set_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/en_set_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/clr_set_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/frame_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_frame_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/en_frame_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/clr_frame_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/word_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_word_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/en_word_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/clr_word_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/finish_word +add wave -noupdate -expand -group l1 /tb_caches_top/l1/finish_frame +add wave -noupdate -expand -group l1 /tb_caches_top/l1/finish_set +add wave -noupdate -expand -group l1 /tb_caches_top/l1/abort +add wave -noupdate -expand -group l1 /tb_caches_top/l1/state +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_state +add wave -noupdate -expand -group l1 /tb_caches_top/l1/cache +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_cache +add wave -noupdate -expand -group l1 /tb_caches_top/l1/ridx +add wave -noupdate -expand -group l1 /tb_caches_top/l1/last_used +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_last_used +add wave -noupdate -expand -group l1 /tb_caches_top/l1/read_addr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_read_addr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/decoded_req_addr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/decoded_addr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/hit +add wave -noupdate -expand -group l1 /tb_caches_top/l1/pass_through +add wave -noupdate -expand -group l1 /tb_caches_top/l1/hit_data +add wave -noupdate -expand -group l1 /tb_caches_top/l1/hit_idx +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {70000 ps} 0} +quietly wave cursor active 1 +configure wave -namecolwidth 150 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 1 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ps +update +WaveRestoreZoom {0 ps} {450840 ps} diff --git a/verification/uvm/caches/waves/l2.do b/verification/uvm/caches/waves/l2.do new file mode 100644 index 000000000..825c60de0 --- /dev/null +++ b/verification/uvm/caches/waves/l2.do @@ -0,0 +1,88 @@ +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate /tb_caches_top/l2_cif/CLK +add wave -noupdate -color Red /tb_caches_top/l2_cif/nRST +add wave -noupdate -expand -group l2_cif /tb_caches_top/l2_cif/clear +add wave -noupdate -expand -group l2_cif -color Goldenrod /tb_caches_top/l2_cif/flush +add wave -noupdate -expand -group l2_cif /tb_caches_top/l2_cif/clear_done +add wave -noupdate -expand -group l2_cif -color Goldenrod /tb_caches_top/l2_cif/flush_done +add wave -noupdate -expand -group arb_l2_bus_if -color Magenta /tb_caches_top/arb_l2_bus_if/addr +add wave -noupdate -expand -group arb_l2_bus_if -color {Spring Green} /tb_caches_top/arb_l2_bus_if/wdata +add wave -noupdate -expand -group arb_l2_bus_if -color Coral /tb_caches_top/arb_l2_bus_if/rdata +add wave -noupdate -expand -group arb_l2_bus_if -color Firebrick /tb_caches_top/arb_l2_bus_if/ren +add wave -noupdate -expand -group arb_l2_bus_if -color {Green Yellow} /tb_caches_top/arb_l2_bus_if/wen +add wave -noupdate -expand -group arb_l2_bus_if -color {Sky Blue} /tb_caches_top/arb_l2_bus_if/busy +add wave -noupdate -expand -group arb_l2_bus_if -color Pink /tb_caches_top/arb_l2_bus_if/byte_en +add wave -noupdate -expand -group mem_bus_if -color Magenta /tb_caches_top/mem_bus_if/addr +add wave -noupdate -expand -group mem_bus_if -color {Spring Green} /tb_caches_top/mem_bus_if/wdata +add wave -noupdate -expand -group mem_bus_if -color Coral /tb_caches_top/mem_bus_if/rdata +add wave -noupdate -expand -group mem_bus_if -color Firebrick /tb_caches_top/mem_bus_if/ren +add wave -noupdate -expand -group mem_bus_if -color {Green Yellow} /tb_caches_top/mem_bus_if/wen +add wave -noupdate -expand -group mem_bus_if -color {Sky Blue} /tb_caches_top/mem_bus_if/busy +add wave -noupdate -expand -group mem_bus_if -color Pink /tb_caches_top/mem_bus_if/byte_en +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/CACHE_SIZE +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/BLOCK_SIZE +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/ASSOC +add wave -noupdate -group l2 -group params /tb_caches_top/l2/NONCACHE_START_ADDR +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TOTAL_BYTES +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TOTAL_WORDS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TOTAL_FRAMES +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_SETS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_FRAME_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_SET_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_BLOCK_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TAG_BITS +add wave -noupdate -group l2 -group params /tb_caches_top/l2/FRAME_SIZE +add wave -noupdate -group l2 -group if /tb_caches_top/l2/CLK +add wave -noupdate -group l2 -group if /tb_caches_top/l2/nRST +add wave -noupdate -group l2 -group if /tb_caches_top/l2/clear +add wave -noupdate -group l2 -group if /tb_caches_top/l2/flush +add wave -noupdate -group l2 -group if /tb_caches_top/l2/clear_done +add wave -noupdate -group l2 -group if /tb_caches_top/l2/flush_done +add wave -noupdate -group l2 /tb_caches_top/l2/set_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_set_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_set_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_set_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/frame_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_frame_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_frame_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_frame_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/word_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_word_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_word_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_word_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/finish_word +add wave -noupdate -group l2 /tb_caches_top/l2/finish_frame +add wave -noupdate -group l2 /tb_caches_top/l2/finish_set +add wave -noupdate -group l2 /tb_caches_top/l2/state +add wave -noupdate -group l2 /tb_caches_top/l2/next_state +add wave -noupdate -group l2 /tb_caches_top/l2/cache +add wave -noupdate -group l2 /tb_caches_top/l2/next_cache +add wave -noupdate -group l2 /tb_caches_top/l2/decoded_addr +add wave -noupdate -group l2 /tb_caches_top/l2/hit +add wave -noupdate -group l2 /tb_caches_top/l2/pass_through +add wave -noupdate -group l2 /tb_caches_top/l2/hit_data +add wave -noupdate -group l2 /tb_caches_top/l2/hit_idx +add wave -noupdate -group l2 /tb_caches_top/l2/lru +add wave -noupdate -group l2 /tb_caches_top/l2/nextlru +add wave -noupdate -group l2 /tb_caches_top/l2/ridx +add wave -noupdate -group l2 /tb_caches_top/l2/read_addr +add wave -noupdate -group l2 /tb_caches_top/l2/next_read_addr +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {1897968 ps} 0} +quietly wave cursor active 1 +configure wave -namecolwidth 206 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 1 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ps +update +WaveRestoreZoom {0 ps} {4798927 ps} diff --git a/wave_format.gtkw b/wave_format.gtkw deleted file mode 100644 index 3f4dd3b32..000000000 --- a/wave_format.gtkw +++ /dev/null @@ -1,147 +0,0 @@ -[*] -[*] GTKWave Analyzer v3.3.111 (w)1999-2020 BSI -[*] Sun Nov 6 02:28:56 2022 -[*] -[dumpfile] "/home/asicfab/a/socet46/RISCVBusiness/waveform.fst" -[dumpfile_mtime] "Sun Nov 6 02:11:18 2022" -[dumpfile_size] 85451 -[savefile] "/home/asicfab/a/socet46/RISCVBusiness/wave_format.gtkw" -[timestart] 0 -[size] 3440 1371 -[pos] -39 -39 -*-3.008905 52 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -[treeopen] TOP. -[treeopen] TOP.top_core. -[treeopen] TOP.top_core.CORE. -[treeopen] TOP.top_core.CORE.pipeline. -[treeopen] TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf. -[treeopen] TOP.top_core.CORE.pipeline.mem_pipe_if. -[treeopen] TOP.top_core.CORE.pipeline.mem_pipe_if.ex_mem_reg. -[sst_width] 293 -[signals_width] 275 -[sst_expanded] 1 -[sst_vpaned_height] 411 -@28 -[color] 3 -TOP.CLK -[color] 3 -TOP.nRST -[color] 3 -TOP.halt -@200 -- --FETCH STAGE -@22 -TOP.top_core.CORE.pipeline.fetch_stage_i.pc[31:0] -TOP.top_core.CORE.pipeline.fetch_stage_i.instr[31:0] -@28 -TOP.top_core.CORE.pipeline.fetch_stage_i.mal_insn -@22 -TOP.top_core.CORE.pipeline.fetch_stage_i.badaddr[31:0] -TOP.top_core.CORE.pipeline.fetch_stage_i.pc4or2[31:0] -@200 -- -@22 -TOP.top_core.CORE.pipeline.mem_pipe_if.brj_addr[31:0] -@200 -- --EXECUTE STAGE -@22 -TOP.top_core.CORE.pipeline.fetch_ex_if.fetch_ex_reg.pc[31:0] -@100000028 -TOP.top_core.CORE.pipeline.execute_stage_i.cu_if.opcode[6:0] -TOP.top_core.CORE.pipeline.execute_stage_i.cu_if.branch_type[2:0] -TOP.top_core.CORE.pipeline.execute_stage_i.cu_if.load_type[2:0] -@22 -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.rf_if.rs1[4:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.rf_if.rs2[4:0] -@200 -- -@100000028 -TOP.top_core.CORE.pipeline.execute_stage_i.alu_if.aluop[3:0] -@22 -TOP.top_core.CORE.pipeline.execute_stage_i.alu_if.port_out[31:0] -@200 -- -@28 -TOP.top_core.CORE.pipeline.execute_stage_i.branch_if.branch_taken -@22 -TOP.top_core.CORE.pipeline.execute_stage_i.branch_if.branch_addr[31:0] -@200 -- -@22 -TOP.top_core.CORE.pipeline.execute_stage_i.ex_mem_if.rd_m[4:0] -TOP.top_core.CORE.pipeline.execute_stage_i.ex_mem_if.reg_wdata[31:0] -@28 -TOP.top_core.CORE.pipeline.execute_stage_i.ex_mem_if.reg_write -@200 -- --MEM STAGE -@22 -TOP.top_core.CORE.pipeline.mem_stage_i.ex_mem_if.ex_mem_reg.pc[31:0] -@100000029 -TOP.top_core.CORE.pipeline.mem_pipe_if.ex_mem_reg.tracker_signals.opcode[6:0] -@22 -TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.addr[31:0] -@28 -TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.busy -@22 -TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.byte_en[3:0] -TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.rdata[31:0] -@28 -TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.ren -@22 -TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.wdata[31:0] -@28 -TOP.top_core.CORE.pipeline.mem_stage_i.dgen_bus_if.wen -@200 -- --HAZARD & FORWARDING -@28 -TOP.top_core.CORE.pipeline.fw_if.fwd_rs1 -TOP.top_core.CORE.pipeline.fw_if.fwd_rs2 -@200 -- -@28 -TOP.top_core.CORE.pipeline.hazard_if.if_ex_flush -TOP.top_core.CORE.pipeline.hazard_if.if_ex_stall -TOP.top_core.CORE.pipeline.hazard_if.ex_mem_flush -TOP.top_core.CORE.pipeline.hazard_if.ex_mem_stall -@200 -- --REGISTER FILE -@22 -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[0][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[1][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[2][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[3][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[4][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[5][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[6][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[7][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[8][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[9][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[10][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[11][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[12][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[13][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[14][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[15][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[16][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[17][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[18][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[19][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[20][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[21][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[22][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[23][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[24][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[25][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[26][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[27][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[28][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[29][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[30][31:0] -TOP.top_core.CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[31][31:0] -[pattern_trace] 1 -[pattern_trace] 0 From aebf325235995eaa92783e40e1f41f862f784488 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Tue, 2 May 2023 12:44:52 -0400 Subject: [PATCH 40/47] memory_controller: Fix high-latency AHB bug This fixes a bug where high-latency operations being cancelled could cause incorrect execution. The strategy is to hold the memory controller in a state where all buses are "busy" until all outstanding bus requests complete. --- .../standard_core/memory_controller.sv | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/source_code/standard_core/memory_controller.sv b/source_code/standard_core/memory_controller.sv index 31d34686f..8116c079f 100644 --- a/source_code/standard_core/memory_controller.sv +++ b/source_code/standard_core/memory_controller.sv @@ -43,7 +43,8 @@ module memory_controller ( INSTR_WAIT, DATA_REQ, DATA_INSTR_REQ, - DATA_WAIT + DATA_WAIT, + CANCEL_REQ_WAIT } state_t; state_t current_state, next_state; @@ -109,7 +110,7 @@ module memory_controller ( if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = DATA_REQ; else next_state = IDLE; end else if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = INSTR_DATA_REQ; - else if(!i_gen_bus_if.ren) next_state = IDLE; + else if(!i_gen_bus_if.ren) next_state = CANCEL_REQ_WAIT; else next_state = INSTR_WAIT; end @@ -121,6 +122,14 @@ module memory_controller ( else next_state = DATA_WAIT; end + CANCEL_REQ_WAIT: begin + if(out_gen_bus_if.busy == 1'b0) begin + next_state = IDLE; + end else begin + next_state = CANCEL_REQ_WAIT; + end + end + default: next_state = IDLE; endcase end @@ -192,6 +201,16 @@ module memory_controller ( i_gen_bus_if.busy = 1'b1; d_gen_bus_if.busy = out_gen_bus_if.busy; end + + CANCEL_REQ_WAIT: begin + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; + out_gen_bus_if.byte_en = 0; + i_gen_bus_if.busy = 1; + d_gen_bus_if.busy = 1; + end + default: begin out_gen_bus_if.wen = 0; out_gen_bus_if.ren = 0; From 16549efb8c94c22aee487242492fd24f5b285b19 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Tue, 2 May 2023 12:51:35 -0400 Subject: [PATCH 41/47] CPU Tracker: Remove "display", add WFI support --- source_code/trackers/cpu_tracker.sv | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source_code/trackers/cpu_tracker.sv b/source_code/trackers/cpu_tracker.sv index 4659ef21c..d44503e07 100644 --- a/source_code/trackers/cpu_tracker.sv +++ b/source_code/trackers/cpu_tracker.sv @@ -179,9 +179,10 @@ module cpu_tracker ( ECALL: instr_mnemonic = "ecall"; EBREAK: instr_mnemonic = "ebreak"; MRET: instr_mnemonic = "mret"; + WFI: instr_mnemonic = "wfi"; default: begin - instr_mnemonic = "errr"; - $display("%b", priv_insn_t'(funct12)); + instr_mnemonic = "unknown system op"; + //$display("%b", priv_insn_t'(funct12)); end endcase end From af8ee22ec3deff66e817fca8c32073dd9bb887ae Mon Sep 17 00:00:00 2001 From: Cole Nelson <35197931+cole-nelson@users.noreply.github.com> Date: Tue, 13 Jun 2023 11:48:37 -0400 Subject: [PATCH 42/47] Bus fault handler (#26) * Priv Unit: Fix handling of PMA/PMP faults * generic_bus_if: Add "error" signal, propagate * Bus Fault: Fix I-fault case * L1: Make pass_through respect wen/ren, revert cache state when request deaserted. --- source_code/bus_bridges/ahb.sv | 1 + .../bus_bridges/generic_nonpipeline.sv | 1 + source_code/caches/l1/l1_cache.sv | 9 +++-- source_code/caches/l1/l1_wrapper.sv | 12 ++++++ .../caches/pass_through/pass_through_cache.sv | 1 + source_code/include/generic_bus_if.vh | 5 ++- source_code/include/prv_pipeline_if.vh | 1 + .../stage3/source/stage3_fetch_stage.sv | 23 +++++++---- .../stage3/source/stage3_hazard_unit.sv | 4 +- .../stage3/source/stage3_mem_stage.sv | 4 +- source_code/ram/ram_wrapper.sv | 1 + .../standard_core/memory_controller.sv | 18 +++++++++ source_code/standard_core/top_core.sv | 2 + tb_core.cc | 10 ++++- .../interrupts-exceptions/build_all.py | 2 +- .../interrupts-exceptions/bus_fault.c | 39 +++++++++++++++++++ verification/pma-tests/build_all.py | 2 +- verification/pma-tests/pma_i.c | 5 +++ verification/pmp-tests/build_all.py | 2 +- verification/u-mode/build_all.py | 2 +- 20 files changed, 121 insertions(+), 23 deletions(-) create mode 100644 source_code/caches/l1/l1_wrapper.sv create mode 100644 verification/interrupts-exceptions/bus_fault.c diff --git a/source_code/bus_bridges/ahb.sv b/source_code/bus_bridges/ahb.sv index 1a8e59b5e..602fc7018 100644 --- a/source_code/bus_bridges/ahb.sv +++ b/source_code/bus_bridges/ahb.sv @@ -86,6 +86,7 @@ module ahb ( assign out_gen_bus_if.busy = state == IDLE || ~((ahb_m.HREADY && (state == DATA))); assign out_gen_bus_if.rdata = ahb_m.HRDATA; + assign out_gen_bus_if.error = ahb_m.HRESP; // Unused signals assign ahb_m.HMASTLOCK = 1'b0; assign ahb_m.HBURST = 3'b000; diff --git a/source_code/bus_bridges/generic_nonpipeline.sv b/source_code/bus_bridges/generic_nonpipeline.sv index 106e51837..49042a1b6 100644 --- a/source_code/bus_bridges/generic_nonpipeline.sv +++ b/source_code/bus_bridges/generic_nonpipeline.sv @@ -56,5 +56,6 @@ module generic_nonpipeline ( assign pipeline_trans_if.busy = (out_gen_bus_if.addr == 0) ? 1 : out_gen_bus_if.busy; assign pipeline_trans_if.rdata = out_gen_bus_if.rdata; assign next_wdata = pipeline_trans_if.wdata; + assign pipeline_trans_if.error = out_gen_bus_if.error; endmodule diff --git a/source_code/caches/l1/l1_cache.sv b/source_code/caches/l1/l1_cache.sv index 293260ff7..f0fac0838 100644 --- a/source_code/caches/l1/l1_cache.sv +++ b/source_code/caches/l1/l1_cache.sv @@ -112,6 +112,9 @@ module l1_cache #( logic flush_req, nflush_req; logic idle_done; + // error handling + assign proc_gen_bus_if.error = mem_gen_bus_if.error; + // sram instance assign sramSEL = (state == FLUSH_CACHE || state == IDLE) ? flush_idx.set_num : decoded_addr.idx_bits; sram #(.SRAM_WR_SIZE(SRAM_W), .SRAM_HEIGHT(N_SETS)) @@ -261,7 +264,7 @@ module l1_cache #( next_last_used[decoded_addr.idx_bits] = hit_idx; end // passthrough - else if(pass_through) begin + else if(pass_through && (proc_gen_bus_if.wen || proc_gen_bus_if.ren)) begin mem_gen_bus_if.wen = proc_gen_bus_if.wen; mem_gen_bus_if.ren = proc_gen_bus_if.ren; mem_gen_bus_if.addr = proc_gen_bus_if.addr; @@ -386,13 +389,13 @@ module l1_cache #( next_state = FLUSH_CACHE; end FETCH: begin - if (decoded_addr != decoded_req_addr) + if (mem_gen_bus_if.error || decoded_addr != decoded_req_addr || !(proc_gen_bus_if.ren || proc_gen_bus_if.wen)) next_state = HIT; else if (word_count_done) next_state = HIT; end WB: begin - if (decoded_addr != decoded_req_addr) + if (mem_gen_bus_if.error || decoded_addr != decoded_req_addr || !(proc_gen_bus_if.ren || proc_gen_bus_if.wen)) next_state = HIT; else if (word_count_done) next_state = FETCH; diff --git a/source_code/caches/l1/l1_wrapper.sv b/source_code/caches/l1/l1_wrapper.sv new file mode 100644 index 000000000..ea41ce7b3 --- /dev/null +++ b/source_code/caches/l1/l1_wrapper.sv @@ -0,0 +1,12 @@ + + +module l1_wrapper(); + + generic_bus_if mem_gen_bus_if(); + generic_bus_if proc_gen_bus_if(); + + logic CLK, nRST, clear, flush, clear_done, flush_done; + + l1_cache DUT(.*); + +endmodule diff --git a/source_code/caches/pass_through/pass_through_cache.sv b/source_code/caches/pass_through/pass_through_cache.sv index 2d590a50c..e8f14e3fc 100644 --- a/source_code/caches/pass_through/pass_through_cache.sv +++ b/source_code/caches/pass_through/pass_through_cache.sv @@ -40,5 +40,6 @@ module pass_through_cache ( assign proc_gen_bus_if.rdata = mem_gen_bus_if.rdata; assign proc_gen_bus_if.busy = mem_gen_bus_if.busy; + assign proc_gen_bus_if.error = mem_gen_bus_if.error; endmodule diff --git a/source_code/include/generic_bus_if.vh b/source_code/include/generic_bus_if.vh index 9634f7e44..31d84e850 100644 --- a/source_code/include/generic_bus_if.vh +++ b/source_code/include/generic_bus_if.vh @@ -33,15 +33,16 @@ interface generic_bus_if (); word_t rdata; logic ren,wen; logic busy; + logic error; logic [3:0] byte_en; modport generic_bus ( input addr, ren, wen, wdata, byte_en, - output rdata, busy + output rdata, busy, error ); modport cpu ( - input rdata, busy, + input rdata, busy, error, output addr, ren, wen, wdata, byte_en ); diff --git a/source_code/include/prv_pipeline_if.vh b/source_code/include/prv_pipeline_if.vh index 9b6803a66..a04de0dd9 100644 --- a/source_code/include/prv_pipeline_if.vh +++ b/source_code/include/prv_pipeline_if.vh @@ -77,6 +77,7 @@ interface prv_pipeline_if(); ); modport fetch ( + input prot_fault_i, output iren, iaddr, i_acc_width ); diff --git a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv index 54494ad14..34fed7aa2 100644 --- a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv @@ -49,6 +49,12 @@ module stage3_fetch_stage ( parameter logic [31:0] RESET_PC = 32'h80000000; word_t pc, pc4or2, npc, instr; + + //Send exceptions through pipeline + logic mal_addr; + logic fault_insn; + logic mal_insn; + word_t badaddr; //PC logic @@ -85,21 +91,16 @@ module stage3_fetch_stage ( : pc4or2)))); //Instruction Access logic - assign hazard_if.i_mem_busy = igen_bus_if.busy; + assign hazard_if.i_mem_busy = igen_bus_if.busy && !fault_insn; assign igen_bus_if.addr = rv32cif.rv32c_ena ? rv32cif.imem_pc : pc; assign igen_bus_if.ren = hazard_if.iren && !rv32cif.done_earlier && !hazard_if.suppress_iren; assign igen_bus_if.wen = 1'b0; assign igen_bus_if.byte_en = 4'b1111; assign igen_bus_if.wdata = '0; - //Send exceptions through pipeline - logic mal_addr; - logic fault_insn; - logic mal_insn; - word_t badaddr; assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); - assign fault_insn = 1'b0; + assign fault_insn = prv_pipe_if.prot_fault_i || (igen_bus_if.ren && igen_bus_if.error); // TODO: Set this up to fault on bus error assign mal_insn = mal_addr; assign badaddr = igen_bus_if.addr; assign hazard_if.pc_f = pc; @@ -113,6 +114,13 @@ module stage3_fetch_stage ( if (!nRST) fetch_ex_if.fetch_ex_reg <= '0; else if (hazard_if.if_ex_flush && !hazard_if.if_ex_stall) fetch_ex_if.fetch_ex_reg <= '0; else if (!hazard_if.if_ex_stall) begin + if(mal_insn || fault_insn) begin + // Squash to NOP if exception + // Still valid for exception handling + fetch_ex_if.fetch_ex_reg.instr <= '0; + end else begin + fetch_ex_if.fetch_ex_reg.instr <= instr_to_ex; + end fetch_ex_if.fetch_ex_reg.valid <= 1'b1; fetch_ex_if.fetch_ex_reg.token <= 1'b1; fetch_ex_if.fetch_ex_reg.mal_insn <= mal_insn; @@ -120,7 +128,6 @@ module stage3_fetch_stage ( fetch_ex_if.fetch_ex_reg.badaddr <= badaddr; fetch_ex_if.fetch_ex_reg.pc <= pc; fetch_ex_if.fetch_ex_reg.pc4 <= pc4or2; - fetch_ex_if.fetch_ex_reg.instr <= instr_to_ex; fetch_ex_if.fetch_ex_reg.prediction <= predict_if.predict_taken; // TODO: This is just wrong... end end diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv index f7d41a846..dc82f33a4 100644 --- a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -78,7 +78,7 @@ module stage3_hazard_unit ( /* Hazards due to Interrupts/Exceptions */ assign prv_pipe_if.ret = hazard_if.ret; - assign exception = hazard_if.fault_insn | hazard_if.mal_insn | prv_pipe_if.prot_fault_i + assign exception = hazard_if.fault_insn | hazard_if.mal_insn // | prv_pipe_if.prot_fault_i | hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l | hazard_if.fault_s | hazard_if.mal_s | hazard_if.breakpoint | hazard_if.env | prv_pipe_if.prot_fault_l | prv_pipe_if.prot_fault_s; @@ -109,7 +109,7 @@ module stage3_hazard_unit ( hazard_if.jump | hazard_if.branch; //Because 2 stages - assign prv_pipe_if.fault_insn = hazard_if.fault_insn | prv_pipe_if.prot_fault_i; + assign prv_pipe_if.fault_insn = hazard_if.fault_insn;// | prv_pipe_if.prot_fault_i; assign prv_pipe_if.mal_insn = hazard_if.mal_insn; assign prv_pipe_if.illegal_insn = hazard_if.illegal_insn; assign prv_pipe_if.fault_l = hazard_if.fault_l | prv_pipe_if.prot_fault_l; diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv index 2e9ab1016..e937e1d1c 100644 --- a/source_code/pipelines/stage3/source/stage3_mem_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -204,9 +204,9 @@ module stage3_mem_stage( assign hazard_if.fault_insn = ex_mem_if.ex_mem_reg.fault_insn; assign hazard_if.mal_insn = ex_mem_if.ex_mem_reg.mal_insn; assign hazard_if.illegal_insn = ex_mem_if.ex_mem_reg.illegal_insn || prv_pipe_if.invalid_priv_isn; - assign hazard_if.fault_l = 1'b0; + assign hazard_if.fault_l = ex_mem_if.ex_mem_reg.dren && dgen_bus_if.error; assign hazard_if.mal_l = ex_mem_if.ex_mem_reg.dren & mal_addr; - assign hazard_if.fault_s = 1'b0; + assign hazard_if.fault_s = ex_mem_if.ex_mem_reg.dwen && dgen_bus_if.error; assign hazard_if.mal_s = ex_mem_if.ex_mem_reg.dwen & mal_addr; assign hazard_if.breakpoint = ex_mem_if.ex_mem_reg.breakpoint; assign hazard_if.env = ex_mem_if.ex_mem_reg.ecall_insn; diff --git a/source_code/ram/ram_wrapper.sv b/source_code/ram/ram_wrapper.sv index 70c7e0716..8a207be4c 100644 --- a/source_code/ram/ram_wrapper.sv +++ b/source_code/ram/ram_wrapper.sv @@ -36,6 +36,7 @@ module ram_wrapper ( logic [RAM_ADDR_SIZE-3:0] word_addr; assign word_addr = gen_bus_if.addr[WORD_SIZE-1:2]; + assign gen_bus_if.error = (gen_bus_if.addr < 32'h8000_0000); ram_sim_model #( .LAT(0), diff --git a/source_code/standard_core/memory_controller.sv b/source_code/standard_core/memory_controller.sv index 8116c079f..eda5fc448 100644 --- a/source_code/standard_core/memory_controller.sv +++ b/source_code/standard_core/memory_controller.sv @@ -143,7 +143,9 @@ module memory_controller ( out_gen_bus_if.addr = 0; out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; end //-- INSTRUCTION REQUEST --// @@ -154,7 +156,9 @@ module memory_controller ( out_gen_bus_if.addr = i_gen_bus_if.addr; out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; end INSTR_DATA_REQ: begin out_gen_bus_if.wen = d_gen_bus_if.wen; @@ -162,7 +166,9 @@ module memory_controller ( out_gen_bus_if.addr = d_gen_bus_if.addr; out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; i_gen_bus_if.busy = out_gen_bus_if.busy; + i_gen_bus_if.error = out_gen_bus_if.error; d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; end INSTR_WAIT: begin out_gen_bus_if.wen = 0; @@ -170,7 +176,9 @@ module memory_controller ( out_gen_bus_if.addr = 0; out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; i_gen_bus_if.busy = out_gen_bus_if.busy; + i_gen_bus_if.error = out_gen_bus_if.error; end //-- DATA REQUEST --// @@ -180,7 +188,9 @@ module memory_controller ( out_gen_bus_if.addr = d_gen_bus_if.addr; out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; end DATA_INSTR_REQ: begin out_gen_bus_if.wen = i_gen_bus_if.wen; @@ -188,7 +198,9 @@ module memory_controller ( out_gen_bus_if.addr = i_gen_bus_if.addr; out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; d_gen_bus_if.busy = out_gen_bus_if.busy; + d_gen_bus_if.error = out_gen_bus_if.error; i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; end DATA_WAIT: begin //out_gen_bus_if.wen = d_gen_bus_if.wen; @@ -199,7 +211,9 @@ module memory_controller ( out_gen_bus_if.addr = 0; out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; d_gen_bus_if.busy = out_gen_bus_if.busy; + d_gen_bus_if.error = out_gen_bus_if.error; end CANCEL_REQ_WAIT: begin @@ -208,7 +222,9 @@ module memory_controller ( out_gen_bus_if.addr = 0; out_gen_bus_if.byte_en = 0; i_gen_bus_if.busy = 1; + i_gen_bus_if.error = 0; d_gen_bus_if.busy = 1; + d_gen_bus_if.error = 0; end default: begin @@ -217,7 +233,9 @@ module memory_controller ( out_gen_bus_if.addr = 0; out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; end endcase end diff --git a/source_code/standard_core/top_core.sv b/source_code/standard_core/top_core.sv index 88b7b5f50..e615789dc 100644 --- a/source_code/standard_core/top_core.sv +++ b/source_code/standard_core/top_core.sv @@ -13,6 +13,7 @@ module top_core #( // generic bus if case `ifdef BUS_INTERFACE_GENERIC_BUS input busy, + input error, input [31:0] rdata, output ren, wen, @@ -74,6 +75,7 @@ module top_core #( generic_bus_if gen_bus_if (); assign gen_bus_if.busy = busy; assign gen_bus_if.rdata = rdata; + assign gen_bus_if.error = error; assign ren = gen_bus_if.ren; assign wen = gen_bus_if.wen; assign byte_en = gen_bus_if.byte_en; diff --git a/tb_core.cc b/tb_core.cc index e99ca605d..d1d89f8ab 100644 --- a/tb_core.cc +++ b/tb_core.cc @@ -19,6 +19,7 @@ #define EXT_ADDR_SET 0xFFFFFFF4 #define EXT_ADDR_CLEAR 0xFFFFFFF8 #define MAGIC_ADDR 0xFFFFFFFC +#define BUS_ERROR_TOP 0x20000000 // Inclusive range of memory-mapped peripherals #define MMIO_RANGE_BEGIN (MTIME_ADDR) @@ -281,12 +282,15 @@ int main(int argc, char **argv) { reset(dut, m_trace); while(!dut.halt && sim_time < 100000) { + dut.error = 0; // TODO: Variable latency if((dut.ren || dut.wen) && dut.busy) { dut.busy = 0; if(dut.ren) { uint32_t addr = dut.addr & 0xFFFFFFFC; - if(!MemoryMap::is_mmio_region(addr)) { + if(addr < BUS_ERROR_TOP) { + dut.error = 1; + }else if(!MemoryMap::is_mmio_region(addr)) { dut.rdata = memory.read(addr); } else { dut.rdata = memory.mmio_read(addr); @@ -295,7 +299,9 @@ int main(int argc, char **argv) { uint32_t addr = dut.addr & 0xFFFFFFFC; uint32_t value = dut.wdata; uint8_t mask = dut.byte_en; - if(!MemoryMap::is_mmio_region(addr)) { + if(addr < BUS_ERROR_TOP) { + dut.error = 1; + } else if(!MemoryMap::is_mmio_region(addr)) { memory.write(addr, value, mask); } else { memory.mmio_write(addr, value, mask); diff --git a/verification/interrupts-exceptions/build_all.py b/verification/interrupts-exceptions/build_all.py index f306e6c5e..47646f028 100755 --- a/verification/interrupts-exceptions/build_all.py +++ b/verification/interrupts-exceptions/build_all.py @@ -6,7 +6,7 @@ import pathlib -compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i', '-mabi=ilp32', '-mcmodel=medany', +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i_zicsr', '-mabi=ilp32', '-mcmodel=medany', '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] diff --git a/verification/interrupts-exceptions/bus_fault.c b/verification/interrupts-exceptions/bus_fault.c new file mode 100644 index 000000000..b29db02e0 --- /dev/null +++ b/verification/interrupts-exceptions/bus_fault.c @@ -0,0 +1,39 @@ +#include +#include "utility.h" + +extern int flag; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + /* + asm volatile( + "mv t0, zero; csrrs t0, mepc, t0; addi t0, t0, 4; csrw mepc, t0;" + : + : + : "t0" + );*/ + print("Made it to handler!\n"); + flag = 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t)handler; + uint32_t mstatus_value = 0x8; + + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + print("Read 0x0\n"); + int x = *(volatile int *)(0x4); + + if(flag != 1) { + print("Failed!\n"); + } else { + print("Bus fault, pass!\n"); + } + + return 0; +} \ No newline at end of file diff --git a/verification/pma-tests/build_all.py b/verification/pma-tests/build_all.py index f306e6c5e..47646f028 100755 --- a/verification/pma-tests/build_all.py +++ b/verification/pma-tests/build_all.py @@ -6,7 +6,7 @@ import pathlib -compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i', '-mabi=ilp32', '-mcmodel=medany', +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i_zicsr', '-mabi=ilp32', '-mcmodel=medany', '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] diff --git a/verification/pma-tests/pma_i.c b/verification/pma-tests/pma_i.c index 089a9128f..41358d865 100644 --- a/verification/pma-tests/pma_i.c +++ b/verification/pma-tests/pma_i.c @@ -12,11 +12,16 @@ volatile uint32_t *pma_ram_addr = (uint32_t*) PMA_RAM_ADDR; void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { // In a real program, a fault should be handled differently uint32_t mepc_value; + uint32_t mtval_value; asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + asm volatile("csrr %0, mtval" : "=r"(mtval_value)); mepc_value = (uint32_t)done; // return to the spot after we did the bad jump asm volatile("csrw mepc, %0" : : "r"(mepc_value)); print("PMA Checker failed (expected)\n"); + print("MTVAL: "); + put_uint32_hex(mtval_value); + print("\n"); flag = 1; } diff --git a/verification/pmp-tests/build_all.py b/verification/pmp-tests/build_all.py index f306e6c5e..47646f028 100755 --- a/verification/pmp-tests/build_all.py +++ b/verification/pmp-tests/build_all.py @@ -6,7 +6,7 @@ import pathlib -compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i', '-mabi=ilp32', '-mcmodel=medany', +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i_zicsr', '-mabi=ilp32', '-mcmodel=medany', '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] diff --git a/verification/u-mode/build_all.py b/verification/u-mode/build_all.py index f306e6c5e..47646f028 100755 --- a/verification/u-mode/build_all.py +++ b/verification/u-mode/build_all.py @@ -6,7 +6,7 @@ import pathlib -compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i', '-mabi=ilp32', '-mcmodel=medany', +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i_zicsr', '-mabi=ilp32', '-mcmodel=medany', '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] From 2b1a48d264227a334e772d500f7e1a6e95bc73d7 Mon Sep 17 00:00:00 2001 From: Cole Nelson Date: Sun, 18 Jun 2023 13:25:47 -0400 Subject: [PATCH 43/47] stage3: Fix HDL issues in Quartus 1. Label generate blocks 2. Fix inferred latch in priv unit (typo) --- RISCVBusiness.core | 2 +- source_code/privs/priv_1_12/priv_1_12_csr.sv | 2 +- source_code/privs/priv_1_12/priv_1_12_pmp.sv | 13 ++++++++----- source_code/standard_core/RISCVBusiness.sv | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/RISCVBusiness.core b/RISCVBusiness.core index 52a5bd1a2..3be27fbfe 100644 --- a/RISCVBusiness.core +++ b/RISCVBusiness.core @@ -1,5 +1,5 @@ CAPI=2: -name: socet:riscv:RISCVBusiness:0.1.0 +name: socet:riscv:RISCVBusiness:0.1.1 description: RISC-V Core for AFTx series filesets: diff --git a/source_code/privs/priv_1_12/priv_1_12_csr.sv b/source_code/privs/priv_1_12/priv_1_12_csr.sv index 92dd66a1d..60a13cbd5 100644 --- a/source_code/privs/priv_1_12/priv_1_12_csr.sv +++ b/source_code/privs/priv_1_12/priv_1_12_csr.sv @@ -244,7 +244,7 @@ module priv_1_12_csr #( mcause_next = mcause; inject_mcycle = 1'b0; - inject_mcycle = 1'b0; + inject_mcycleh = 1'b0; inject_minstret = 1'b0; inject_minstreth = 1'b0; diff --git a/source_code/privs/priv_1_12/priv_1_12_pmp.sv b/source_code/privs/priv_1_12/priv_1_12_pmp.sv index b364be65d..61b2513d7 100644 --- a/source_code/privs/priv_1_12/priv_1_12_pmp.sv +++ b/source_code/privs/priv_1_12/priv_1_12_pmp.sv @@ -51,8 +51,11 @@ module priv_1_12_pmp ( end // Core State Logic - logic [1:0] pmp_cfg_addr_add_one_reg = (priv_ext_if.csr_addr[3:0] + 1) >> 2; // exists because TOR is weird - logic [1:0] pmp_cfg_addr_add_one_cfg = (priv_ext_if.csr_addr[3:0] + 1) & 2'h3; // exists because TOR is weird + logic [1:0] pmp_cfg_addr_add_one_reg; // exists because TOR is weird + logic [1:0] pmp_cfg_addr_add_one_cfg; // exists because TOR is weird + + assign pmp_cfg_addr_add_one_reg = (priv_ext_if.csr_addr[3:0] + 1) >> 2; // exists because TOR is weird + assign pmp_cfg_addr_add_one_cfg = (priv_ext_if.csr_addr[3:0] + 1) & 2'h3; // exists because TOR is weird always_comb begin nxt_pmp_addr = pmp_addr_regs; nxt_pmp_cfg = pmp_cfg_regs; @@ -139,7 +142,7 @@ module priv_1_12_pmp ( genvar i; generate - for (i=0; i<16; i++) begin + for (i=0; i<16; i++) begin : g_dpmp_match priv_1_12_pmp_matcher matcher ( {2'b00, prv_intern_if.daddr[31:2]}, pmp_cfg_regs[i>>2][i & 3], @@ -200,7 +203,7 @@ module priv_1_12_pmp ( genvar j; generate - for (j=0; j<16; j++) begin + for (j=0; j<16; j++) begin : g_ipmp_matchers priv_1_12_pmp_matcher matcher ( {2'b00, prv_intern_if.iaddr[31:2]}, pmp_cfg_regs[j>>2][j%4], @@ -269,4 +272,4 @@ module priv_1_12_pmp ( end end -endmodule \ No newline at end of file +endmodule diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index ef0f7b1d1..9a78a2c39 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -219,14 +219,14 @@ module RISCVBusiness ( .ahb_m(ahb_manager) ); end - "apb_if": begin : g_apb_if + /*"apb_if": begin : g_apb_if apb bt( .CLK(CLK), .nRST(nRST), .out_gen_bus_if(pipeline_trans_if), .apbif(apb_requester) ); - end + end*/ endcase endgenerate From 641d74d46a8e4f0b2e9a1b5021b91f0062cc404c Mon Sep 17 00:00:00 2001 From: Vito Gamberini Date: Tue, 1 Aug 2023 14:00:16 -0400 Subject: [PATCH 44/47] fix: Incorrect config integer comparison form (#27) --- scripts/config_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/config_core.py b/scripts/config_core.py index 3019a61ff..24ef976f7 100755 --- a/scripts/config_core.py +++ b/scripts/config_core.py @@ -152,10 +152,10 @@ def create_include(config): if uarch_param not in free_params and not isinstance(uarch_params[uarch_param], int): err = 'Illegal configuration of incorrect type for ' + uarch_param sys.exit(err) - if uarch_params['dcache_size'] % (uarch_params['dcache_block_size'] * uarch_params['dcache_assoc']) is not 0: + if uarch_params['dcache_size'] % (uarch_params['dcache_block_size'] * uarch_params['dcache_assoc']) != 0: err = 'Invalid dcache_size. Not divisible by block_size * assoc.' sys.exit(err) - if uarch_params['icache_size'] % (uarch_params['icache_block_size'] * uarch_params['icache_assoc']) is not 0: + if uarch_params['icache_size'] % (uarch_params['icache_block_size'] * uarch_params['icache_assoc']) != 0: err = 'Invalid icache_size. Not divisible by block_size * assoc.' sys.exit(err) elif uarch_params[uarch_param] not in UARCH_PARAMS[uarch_param]: From f86bd96d1e7eb8c91c22dc0c667232f8f7f2aa9e Mon Sep 17 00:00:00 2001 From: Vito Gamberini Date: Thu, 10 Aug 2023 23:19:26 -0400 Subject: [PATCH 45/47] build: Disable verilator error for ENUMVALUE (#28) --- source_code/pipelines/stage3/source/stage3_execute_stage.sv | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv index b1921c53a..af4573384 100644 --- a/source_code/pipelines/stage3/source/stage3_execute_stage.sv +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -237,7 +237,9 @@ module stage3_execute_stage ( // TODO: NEW always_ff @(posedge CLK, negedge nRST) begin if(!nRST) begin + /*verilator lint_off ENUMVALUE*/ ex_mem_if.ex_mem_reg <= '{default: '0}; + /*verilator lint_on ENUMVALUE*/ end else begin // TODO: This register is ~180b. Not awful, but can it be smaller? // PS: Does it even matter? Synth. tools may be able to merge regs. @@ -298,7 +300,9 @@ module stage3_execute_stage ( ex_mem_if.ex_mem_reg.tracker_signals.imm_U <= cu_if.imm_U; end else if(hazard_if.ex_mem_flush && !hazard_if.ex_mem_stall) begin + /*verilator lint_off ENUMVALUE*/ ex_mem_if.ex_mem_reg <= '{default: '0}; + /*verilator lint_on ENUMVALUE*/ end // else: retain state end From 2f0807b803a2cc392d445e24d2d594f0c0bd4a81 Mon Sep 17 00:00:00 2001 From: socet113 Date: Thu, 21 Sep 2023 19:35:15 -0400 Subject: [PATCH 46/47] Zfh initial update for addition and multiplication --- source_code/Half_Precision_FPU | 1 + 1 file changed, 1 insertion(+) create mode 160000 source_code/Half_Precision_FPU diff --git a/source_code/Half_Precision_FPU b/source_code/Half_Precision_FPU new file mode 160000 index 000000000..41d2840df --- /dev/null +++ b/source_code/Half_Precision_FPU @@ -0,0 +1 @@ +Subproject commit 41d2840df17ede2681758f0953f29392638d9639 From a6bdb1f65fb496fca50ca8a18531d7aebebc7b2b Mon Sep 17 00:00:00 2001 From: socet113 Date: Thu, 21 Sep 2023 19:53:14 -0400 Subject: [PATCH 47/47] Zfh initial update for addition and multiplication --- source_code/Half_Precision_FPU | 1 - .../Half_Precision_Specs.pdf | Bin 0 -> 247692 bytes source_code/Half_Precision_FPU/README.md | 20 ++ .../Half_Precision_FPU/RISCV-User-Manual.pdf | Bin 0 -> 615016 bytes .../addition/float_add_16bit.sv | 198 +++++++++++++++++ .../multiplication/float_mult_16bit.sv | 206 ++++++++++++++++++ 6 files changed, 424 insertions(+), 1 deletion(-) delete mode 160000 source_code/Half_Precision_FPU create mode 100644 source_code/Half_Precision_FPU/Half_Precision_Specs.pdf create mode 100644 source_code/Half_Precision_FPU/README.md create mode 100644 source_code/Half_Precision_FPU/RISCV-User-Manual.pdf create mode 100644 source_code/Half_Precision_FPU/addition/float_add_16bit.sv create mode 100644 source_code/Half_Precision_FPU/multiplication/float_mult_16bit.sv diff --git a/source_code/Half_Precision_FPU b/source_code/Half_Precision_FPU deleted file mode 160000 index 41d2840df..000000000 --- a/source_code/Half_Precision_FPU +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 41d2840df17ede2681758f0953f29392638d9639 diff --git a/source_code/Half_Precision_FPU/Half_Precision_Specs.pdf b/source_code/Half_Precision_FPU/Half_Precision_Specs.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c73f0c95af98a3501156b8f0a68c579cbb5b0cc9 GIT binary patch literal 247692 zcmb@NQ*g0`Wvtv8y*tTukwr$&1$F|e4operrYkh0&b~nE$-kLRQ z&Z=kD#XFwgzn)1dFDy#SNXHCAI&^e*bY6O&H#;-}!wO&k*cw>C@bCcWrHyS&oy-7i zf4UR_^rGfgPR0&@j#m0k#=^#iwnoMPK0X*nCkJDFYZy1+YK=*Utr6s{18OpLe{^IB zlbfmO`lAFTy0r*3i62JvW)Ugb3BpoMB=5|L7m2##r=xgqSCPs;Bw=IdOy2C@oSyX< zYzGo=$eLc*T^OaM;B0FLcLvUHN+XdrOz;l!zQE&LvOFr! z{pU8L?duirkG;QhhClKGp&>ptl7cc)x_okcsZLZG)Q)>6hhy9X$_FSJc+VrR zR5#v0CA5D4J8`0ad0?baoS#xUQC}aX$R5pe+Clu;=0+qU zS0t}%1n08|JYY;W^@DcgVeGrz(HYH9eZSCj$Hyy8)7==4@b7f?7m=>h10thTVSWSE zbD{fL;r)2Il_|U7{NMPMB-gO%O{5FxFy@hFE<&{5JEdh>)$k3(w`?4L==QLInp3V? z^xGW_$Ic^2;mG^0Tvn7&ROAb>-%Uj`TnQaV@3FwjZn@Qj%MGw6K3TL=~ZB(%=-!gP~Ho>g0V=-aX%eW{N+npfSSVJE_CP13pS2CR8RL zmdD|og8^|8jqdC!QR?nj!5SmT)=@%)G>2G$n;ei~mV2X#Mwswhv-zk?^R_qw=!D=5 zXn`FRlM$X58)d^q6z-wqRWBkchVU9>)yx=qS>;j0&rhKC%%uB&^A^t}QcDf6bIpVB0V5 zjp7iMc0w?1n~gt0w%aYeFT4|-QL&ys?oYB$U$4zArM-C4C^vWaM| z^3ARxH*&Kf7O%S5X~`qWCEofYNoL()?omFqYQYcyhsuj}#Wa z?lmNdBU()TK-S9uSJvQMQ5?`Z&?P~INX0o+wrN45pMXhzUhB#xjeO`&RaJGPnXt<{ zYzQ9iL6K8*(2+2d>*cZ$T)8o;QZ}?Yw5c=B#-)1Lz=hZlZ5vAWFmlyhEtX1xQ*Gp_ z)d&VtK*d3*0prWfVol&0grSUnSE8S!H!lVe!*_>F{0o_)g$CxxZwvLDA){47?A3_) zxKEuZnO9$Zh>dv01iyB&jR3GCTTP3wJO+Df~ z6y%l$Cq2|_6RLzlwS<5;Jo^^gyw*UM?K6biiumqRsS-*>c0*ZMqmcQLhWWIP-1(U6 zdP}Wga6^ThkVI5Z^-@MdBlE8s2~ASVfvqh#n-GeT6=hCE5L zpl^@;Px*V4ngcx<&Mn!r;g4Y;nvr- z*rK_KE({IzXh6my4MlkPn7j?D1eqX(-FQ_q&!rXWx6&DJ{IXL7x&0f|Pjy4Fc#-TR z`4kyT(y>{J<#c-4{LTBDb!N5`LH}`L+3|HL>HZW@cc?d-rvtmJj0Y|Q<@pZQH6ZZ z`Z}94G7+3x2|bjmGj~3~6q-XUqfgr}Kz)aK?3G^Y@qN2g1s5sQcyDjiW z<5_KH)h%zaJ84nM50a&U*@7Nb)L3JjBgCte>Xa(_Y7ux`M_tfRkDj_}uajI4I+(xZ zFKSSO1Dh7oB8iKsxn>@5<(zvp%=VlNq#%#tK?4(_FNZLgB^5I|fE95gbAXUYECh{d zT*qG*A%CgCin7r317AbXR~wH-10z2OQMUc6r%|iBmG>L<`VA2tXpAyDFgDRL95<^` z>>mvwLmyPoo~cfRfA4ZRw9R#%Ph+ru)1O#yf+rWq!e%}tmX`tnY+i?(A3LCMUOU8a zFqXv;aexOd7ErPY7MF}^CLA3^9*LC+!SZVX;CvO&Bv{dnpclBbh|kvDOBPJ9Yp(Sm zAF}uZCgax#; zq33F$$QIngCi`2WowPoqRwC@|;ig|;;3k;XVeZD~16>8k$?g{LO zZ-mDOnBm0QcUfcfhHgVQTD7l&yTU19e&lGHFXtOM1{i6E+0NgaDfE8IpT!I2qVT=` zu>2x7&>GX7$YLoK@HqKqYFJ86LK(=9g&?NFBVrT|T1pJvJ&jSr6j!W1BDv0AwP+fT zSQ1Z;-;USEit=DfAmUEzhsRWD)~#9~;{>SaTocY!TQOseF4q1C2C}+ALFZrMb`hYf z0o8ggaETPEWFx?>Udq;KG;sV9jx3ApwX#}lQ;Gp(isEf!W_mlsQcA{)*dr-^qehSo zj|R*+6mgg}sNs*(0K^wAZ}K+J5@53$X*13U$WN(zU&s}Z8{H_tH^Q@f-r6kQI+&6& zhP=~5j7UnRED7&SMzdyo_Yhf;=Y0i}GCa1*v>8rB3VZb*_#cJ)Q&p@4OG3fL8xyt8 zG%tP;>F7mE>ySgfmuP%l(P(`P)?)tRX86_YK@ctS1uto1Yg*r`{IGzbcZq5Wu z&?i19A(a9l5qR0zX{0m2g}+BcJa%qbfdS0~UxkVN#5WmqTcK5Lot{3y8H6Bl^k`0A z4zXp|ac3YWN5C9;x&4z5`XFd<*rs2iBp_Rk`&vZLol#@N-SqM+y{{a67ORj^d_%L!MqK# zC@wn3``xfzI`By`{tcayXQ*!;6m+X(R|iGMp*ILhuT{s8Ni?-_NB{n^0ESt+goXxY zFuzt4grf%i=VX&Hm8>{^B z*y}N33G8wM3i__-0LePwwX*hTRvG7aX>Mg^nn#Taa5lXYQ1i}<0d$A3Xf(=;_Qb-+ zMlb^4(h@zS*mq9X?^dYymTqiuIf)+Km%)85#GgnN6KrXdYyo)8_=^iF2iPEhVFg=# z>@~)zqaGF*kgF1%)?K@rnNY{`W=vB=G_|eZ<{+i2WK4WN18Df_Cqe+}nj5k)^}Y@2 z^d37kMNB>kA6;HWqWQ$T^G%2zQfD-q0*LpMPkTVTree5zqrVt6ZOmCM2p_3*wKv3# ze(09^_osZ^Bf;>kLp=9uS_$=wlCWzFXDd>sRi34-fH{VTFctEnd7lf$2;_7fS7c}g zI9kyLA1hP1QXcyuz4ZKskUp6u(Mj#&%TySv-?Q8|6MRK?Lf}`ZWIiPnd{#~`>)k;J zMLEZuH-iv#LZlZm7(Ody2qZ=fTG_P!tM zzi;6LEe(ZTtI^&altrWWcAKrrb0GTSL@#WdB&zI|*sSiQ`&4P=Y`r zd1ni5?W+v5BfTir)Z*eZ1A_U0?1yc>ZjZzAjb}iy-VCd|Rt%QlXp6s=6=PguJf$Er z7uC)!wMtl6BUbAb(fAf5Os6!w@J9WG?-It&E7(Co@-tr?C=9o&FrP&7$^*UbnY}h) z9aO=^fmhQ$zLO9ZL0oRPz@?fRV-oYmH!U^!>yu13W>ff-%m|wg*5#$BzG)DHr_H}g zbpI(~GUGv^=Rq8K@KW##-gO}!bnM|`lJN(>#ZdJ*T?s_1TuX&fV<5vG5jRLV=2~DE zEm3d6_;4aCWw!_hZ8+<_nqp2YtWJSGMX#r>P0_9((hG$rZ}*!&@;Rd?HfC5$)9Uzv z$#+7C+V^ma#@segeD4*Jok8+t?4y|o2a{VKGlK*0z40%gQ_s{I7-JiwzajEZ>o4~F z3poEeGPAKT{dM&p?f>9XWp_Jc0KL4v=|6rQjBT6%EdOLwMPo->X9q)LM*#ajJA`a) zoc>&Q1pLLde;`)I*vMR8(AEv0$?&Ixjgbk!&dH<=L$7G-^oMHybkPgg*x3FV&-l+9 z`~lp*1^jjXFEEyOur*XNb^>VrQ6Ve}pjS3_a{_1s=!I;pY#o&B^bL&xfAO=BBO`$E zujl-Eu#h7YfRXVZZ9cv~5dBBjKNwrtof+^)#XrU* z4PgRs{;SCjVEiYW|55p0LjR%sFQu9Q#(&Pz{?{59|C;)*Mf^?oOn(cM1u*|v{9lM~ z^Vgf87dCb=H#Alh6a1gaKRd-$S!oR;bf%lVdlrQVK>C2=0S%bv7DoqD2$%4a2MGxx za}fp?MMI`kF;eaW77HaZ6hVUx3@})MzC)59=~oaIIogV{gubq3z2DCG997?0J84~6 zGrjg+=>U@d2ml*!QUms5$yY@{K2A7b1ZGf)QC_l;6%E-7Aa&%hyUH-8C)rf;8?{mdF4) zdZt)dc*&9mVn9pRojM`Qw+d$xLBqZZzXJh+N*dzqazKcDa5+o<$vz(}1mD6<#lCOz}e1Q)}A=~ru;A_7dRZk=uxe_4%pd8xj9Z1&o_ zwuA4Z^vOHs@&4nM>|!QIASy?F$LgYrz8eGRi9{pwTVqf151=M%2>zqJD!FR_&>c8X z({|Q9eu$M$!q7e(Ic(&`g_R?4t|cAyFZTD`0NaV)5YFfH$*Xr2?h?%J{=^dK%*G-| zC8Z@&!c$Xl2&pqhpU+5N2YsC8KgS1YvU2+{Z-amRQ23Pu9TaZX8O-Ca7Ds0U6Xd(J z=ZnQg4vFoPZHZ>)tPA8F%qia{0Xb&N3nWAgN@DZf1H}7W6NwRKtf$MYI=55Ef3;>W`e}*{~AziuvA~7Mqc&Z1)2>xGn#ftMK4xQ zvo5tBbXEU?8xLW$4|^Za&ZiAai^+zy2CNlvC92c!d=P$*?AEP|Y8&l30I}ck#=wiP z2gVoA7k?*~V5FBk4q5}uG60b<1wdj&qKt$CbprJnggn5a;9n-^L==MLKBQ$pSs$}1 z&?SLKCYy*R5ki8gNJ5$37x=76QnuOnAOk_*r!MNIfEpF}~1 zCMSs}BDf~YDcR2N&hIW%tO#Fbv#3^)){(6t*DdTV=}j+|$eengdN5v{SQ&4be4gq_ zDwnj9>YA!cmrJfo=qvfVi~Gi)-(GmbJ&SVo)7G+>*-G>$Y*nspeG)32tKO-LIfIrFP) zE=y_W_=>hFd8-Y}8r4h-WgDkawG-$x*#%m)T9sS{<)rA<=yhuquJid6a;tNTdc}G* zeJA>60qc5^0-60~`4RcO`Z@ZAdxpdn@s)E_g&Kw2BOHb;_7S&y2byGc;n@-i64B!_ zN~??8%6c6$O*3$o$7WQ$o4d7OhhV_t>snXlBaypd{=*%(_Gx-^$G zO0?%R1nQLP;A+-QXLWCb>q8lq+J@evAr=RQjngGt$IaE{v^y@nm)_hN+I3qPowA)X zPY6#$?_}?cki4O8q1_12t4=Nyok8A9$mhTI+NN)#4j{=r$?F#+N_Vo=rzxl5rwd`> zFmEsyX|WvU?DyL69OCJW=%RGp?D7|XY$P9OoWD)UtVEx<3|S9tt@5sYw!Kp$N+HIJ zMUEzwCZ6ET!0Lph)~d#<*0v@*8(dplm-2D(h4V%8q4v1+RC?3D=zoHIy1Z@OuiTux zt-Q;Dae!4p|AHw369rR&p=7DB~(?)-D z#G<2NsUo{@Z~Niqxo)niM~4R+n2Qjnx(#sYE(+j|P+b5&Lqan+fMT>fWlaTT@R# zm)*rr#qX}WE>NzUTc;iOYUu{*I%~5vyV})9Q+EY-t(~eDJu3Q|_Iilsu|}~?Ol1w- zlC>6MOQmN;XZ|>`8OPmco~@VJ>!TYhy*t*v3&7E!!;pOVXn0+|W6$!I)dJST`kQ-I zgEQv^XC1w!zPi4`m*ThaGh)GF#iIkU&P&{jE9_t;;xBH_n1lKumJZtU|-W8|hpSe#hvle5!>P^fo$cLy8S)vujNh4)qI=x6v&nnvz5O`{S&pdleaeI5)9lmm;-G*WMvf|{;(PVy z(#wiTdH2Ib-iqE#W0+}dNShG-SMqDE+#vW&`3$valjxaPwb)G5Yt)uU{loChWK`bPJ)CdLPu3?!zYZ5%&96&JBepAhzCZbY z=D!<0jK-f{$i2uV=SF{b^@9Gmb0f1hIo^2=x_|$_J^w#!=wH^9>5nP@w_p9soBq?v z(n|;l3FNF)o{ju@o4gcw zF-B!uoyQDx-t1HRGvCb5FTCH+UZ!5>ygR-?{Muv5;tlpd{4pH5I4F~{Iq6vgMO&aS z{Fr4RVtErsM-r=ZbI5iuNGB2Czj$w-8({Us9R`6fS?w=+pD4Tp@DZoEz%`M-$EJb; z9m4}v$CAe%gXWn5A05NqkwS$ERwbho-~!oG0l`oeTIOS=1wjpgl!uMN#XCf%5pHJT z(JidarDN0nl)*Iq3S2Uq`?{YTPa*7_n*n1>?y73DdvOVRO1*9Ohw2paN*jfbz#Wc- z6%LO_dI$B(l(;J$6~u$M-A>3X-j7;Da_W3>zG} zCn^Ly1*wfq!ixK^LhR$@`vu;C8H#G59Xak3hP;K9&Tp!*>|g-RXVOE#;lwANs<-cr zK`>K%(bWPF#*f%AVL{`vMKrm%r3)O%R)tAkq<4i-fE@xlTL>ChyF;_=?3cnQZ-?%_s)-ejpF zMS6q?%=7* z&&g-vZ9fT`r$bv}o_)N!Mhv`gMyLs{vfz2qVn@gkHSQ7CvspXdlvs{%RiAzx3N^?$`R%dO!oTrm*ZDjc{In!aI;7embGZpF!|_KU(L9InkUyvc_+l9Kg4B81!H$S<`6%dpTkn;oMW0?L5wHkX zA~_~gt^=@AKyfFk_;Xf}%5q`v;~@)a19?O??fjy!b32h~4`lg0B`WdtT&j7Tff>%1 zGG->5#|pP3*owF1&FLjDYZ>Fjj~7$mU`cXC74X3(iFHK-6g`&-mw_x1D{~&R9(Nbu zmT8x5t7MnyIPQYf zL@c_?82pUQMBC@~p`HM|eREEH`7J==w` zigXIvQJz%v2NV-ZCh5%H_bjmaG$$g?IwlcdvT-tP@h^p_iez>#`D&cxGb(<9Ef_zjAVL> zAXh{n5C>8bMTHQC-0_fBJm5s)W6pKvY<1b2^N=fPGX$U+!$Vg>>C!;*M2x60@rME+ zQn%cBkhhyk3xwWfgjs6#UGUy?fg1!Q@O=TMG|*3^0clBnBdI|MH#oQD8%J)|zML9CED2CkCuQ$w;eQj<_l!>crUj35!c zdNrn$5Mw<9>5yc-T>a!#E-UI5*vi44;f(r)RqzWO7s&JexBBmOtsMT9yBclCHo6l{Y`#;B{iz_e*c3+{m}1zxRhi@l&931ySztx1j{4StHUbp;s(#Ra7XNkqE!;`Yt^_`?{()DhhggOX>G%aVMOx030SlSypUtQ6Tw z9igsFKYeJl&>P213?u~bZNB0xF6Sm4M%2q!>lofv%QB^8GRb(l)7v5#bmTJy@ zo>D$|y-|65`-J-h{$v$O+#B0vc!?s7vJfRc;5sO;D5xl=(sYiosJBQyLtdh;0JVs) zP_Y2E&}tsGShDzO(YhG2$hwrhSh(o8s9JHY+MBmsDzH#D4>iAQnPh&k%u#qNpE%31 z0yleT?o-k(cAsTZi}7PpBeF2L{73m?^s@0Ep9X(vz8=0FyuP$P+T7_3^^N*ljBLxa zMfM<4o-l2Kq~ABzBSAlKqh8;yEQ3+MHcxShmaGvl&DfBqTO0L?RBK!zg4H1s&lU z`8|?8f*U1=^_*#z@sbfH&6bwM;y$%#(vZG3^>eCmNU1lpw7 z1oG!qCUtseW@)A^gDo>b6GMHPzJh^$-#)pNh?J{T{t?-v%cQExvWkStWOa=Cqx!kJ zV^w4|qS{Vl{>IDNRBL&CJ7+rAnzzI9;bmg&`hE*ti{OUE2DM9Dc4hXMP3IP?XGC{s z_iE3`@07p@(2e}QJwKt?!WhFK!}0^h(NZOvk|Jq!?qcr=&lFf{T%WBgE_04x&Sb7; zj?X=-^C`y~mmbb!K32E0gmM|BT$#p9va}Y%b!$wSRBXCQ?RBl+PR@?W9c*n+o$77H z?beQ+M>R+Imp${JU68|u9iu3*DzWT2H94<2p}fA&4mT5b>(@&s)|a$5+XtVsqf?>h zjgzGtsi&F8@5#2Ux#a{awIkQ2zK*^DcCPjb=c>on~_aXXkz%0APQM!j|$I))rBWwz63(i;W6bgudK_I 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�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 z%0D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 literal 0 HcmV?d00001 diff --git a/source_code/Half_Precision_FPU/README.md b/source_code/Half_Precision_FPU/README.md new file mode 100644 index 000000000..bae3740be --- /dev/null +++ b/source_code/Half_Precision_FPU/README.md @@ -0,0 +1,20 @@ +# SoCET_Half_Precision_FPU +SystemVerilog implementation of half precision floating point unit + +Multiplication: + - Functional for most numbers I've checked by hand (Including infinities, qNans/sNans, and sub-normals). + - Current testing is plugging numbers in by hand so have only tested ~10 of each type + - Khoi is writing testbench so that we can finalize module. + +Addition: N/A + +Subtraction: N/A + +Rounding: N/A + +TODO: + - RISCV Business repository + - Make a folder in there and put this code there + - Allow the user to choose the rounding mode + - Add inputs which accrues flags + - Things like divide by zero, or other exceptions diff --git a/source_code/Half_Precision_FPU/RISCV-User-Manual.pdf b/source_code/Half_Precision_FPU/RISCV-User-Manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e4a4634838d4ed270f74a1faa67416ec027d3e05 GIT binary patch literal 615016 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

GFSaX``dG8k` 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 z zF|2OFGqGg&B5PjVr4p`~6tI&-X!YVPD+((S7 zkz8mPPf7)cbT6X z8siij-qTmSs82I?%h04iwCFkqr*B-qUV;A{IIN!vfZ@@&s|2WL0XqIj7^sL}tC_c-r$yM>5BzSov$l%5%1t{~Y&D-) zomvHH-Q}Zy=!U1v7>23*bfed3sRI)7yR#eDKRMPo0>fS^ zj%j!3hr}3nl^2L)U+VGe&>#UT_B@%Nbb|W7ewN`cRc*n zMr0Ax=?sL~MfuAglRy)Qo$wX>4?FJ1gfwV?)0#kA17mfYb8)M@W*DJ{Yf-HGhE$6t z0Rd->Vs)f2>VY`F>`xZ)9$~HS1?8&hV_sr@F+|f5G)ozG_33MsY%We5&k=ULns z3=`2IW$Efa=Id4&Dh}LmJx2LLivtQ$Nzxq)2}KU--)GKcFvv^Sni^=Ed`uzLio;u= z;Hf8H-W9`NJhin4iXuBR^w0dlnBwk*fh$g<43^I3MIAM(0@Kq-b^ex}Je_0?@VNIz zcbZ_61L%OO);duKC?G4(mH<@}!A60rJzKPvMKgC=f;3!z;wwpDzN2>Ac=7?0?>*da zzb^6`iT@BiEdMcc%>3{CuU=Xe&P3uy>m8q7!FWUk&fH)SSR4C@NvSDKEPJyNp{vhs z{A{=?N;!Zku1hb^H=vN^kvwJ6WYL^u7LlbLcS=$XvC{adC9B7`)8kI8geGNBRHy!1k+B2t#qC)&M9SpT;$OrC#!5D55c#KaahES|L*3a z1F?oJ52<;s2V%$W`?{yvh2X7g*2jG;DbM+D!i+3QVt~e*)Y|H*4C#REZY!hY<-Abv zKO(d!S+1z%d-iF^fEm&rvWL1NV8VOi_T&9v>V_Sr*SJJY6xqs5P1C?Yy{Q6$y~kHl zjdk*?6T~IT`bi==EJc;Pxg6ZR=iUB6zBxxyT~yncOBj1Vmy=TYkallGhdG~x4d;_f zRgslVCn}r;26fBASfs^7L?pd=>X@a37b4&IM&RYJKJ4N1b;3up9_ndPRSt#hDu6XD}D4Qwd}7Y2t_Yk z=>alTyRR{rbdds%7@oyyK`-|bZalAb}T?UA~MWnOe`gVzrJO$8+-)kSbIc##z|U=&HNB`i$y#6Sdo*{>0D%aj{{mG zhSqE{;hKp+Xt&^Z*BlaBlzq7Yjg7Ojyln~bYH(D9MxY;uJEXe4{-QyZj`Mol zl!GjV3~WX9PzUfj(A8+G!-^M|o_0tAaKd+1jU1jJ(${Kq$DgBeC5=Gb5F2?=Pg29X z)w|K4I+<*(ITMc5lrS%2!Ri&05R^-WRD1Mdm;yb`aZNc7%2F z+~%LlS)1(aByzH8nK)d}OIOy`P?KU%oG51o;p=B%U~I=-V_! zDCDopvm#*YFw(cEfxe0j%bw3!)WZfKt#s@m#1hAzw2Ux*-XGun3{d_#cBdY<&WE36w(fhWLO4Ko1&wdPi#3~D?3CibM` zl%tcrt`AS^D6{p{b9kR}tV76OL-WV(jV0sBwHYl9|3Scn+G1?N#$yiMMYI>GVT1C~ za;D=$$};Ek0~`OEWn2u#g(%2jbRGmMKfNB}7F&w{LnBY6fPP8V`TTdtyCfGIQv49V zKAmM~>IL6SB$?crBUfKgHgiDU1p!kKMr{yFJ9*r%$qS zRTDG(wHY|BnP-z7vGkhd)+U}Z&`Ief{N9G9>Ddn@_KtzW>Rl7N|v%`r| zalBdudq6E`G5sVG5%ZSQj_3yq>7$SXtNrSg@~JnJR9gc&1f*`BSvlTv^%E`J@zBR$}u^ z+QD8V)!@xFQE3#gw*-sKdDxuoh&zHkVu0vd$`Tb+U9;lcO5JZRUuW{{Ifh5!4n`*R zJ6zF~17!oueIv!3@~JlnX=q;ZOsp?Wzt%ylj}( z;PWrJ%n@f-F*tfIRhWs)iUMxudWtw*lne%EF5-(VD&DAsO7GZpc%c4N;Hl#!f<`JS zPXxS5nrVP!EBdV#8oK{3yUFE5&*uFhZRtLecCeF1S2{8AD4QTeWP=Jr?>RwbNIXMK zYh#vm3!ar;=bYuyem}rjGKxdlwW-eIFG~Y6jO$!TlymoXFDb*kIk_rY!Zc8=1}zqa01oh)!uYT`iqTBMp;Fsg)M;3U2{u zLR*+q;wp7QWbbht3zPe)nBO4sGaHZ{5i?av%cw^fq()N&AMEg8<^{x>H$pq7J=_km z&yTfu$|KkK$bliErl4YG@y)}Nvh0AMc^Y^|?*^;=%lc~GY~PYO-1|N9q655Ilfdk_ z&Dn)*da0@U2D24I)7s{lN86?FKqiZ(Ixo+0DgONLn_jE#b>GMvUSbwn?vZ+;pT;eG z&iQKbc}D9WkL>f$$csQbbI6o6r}E5F#?JYgbh_41$$1W%+=$A6eJVF~_V0l}D=ZG^ zqP($Qa#=&OOTdb+0<3kxvFde(ZQdBOePZQC3vbhsd9GuqxI;G17f<${(vXMaP+JXV zt0{H!dB&u*pq@+=rgjNadGU22*L4Jz!t~HM#*aFYy*2mC$wbY=x{^};?#*twhHnEx zyp1XXD^ZDi)fr~^+X}{Gl%4A<_zC0VZ)5Bq%c!1Va<)J)+r&vR2-rC7gVkdU*ou&M zK*4yWKhwwx!FDq!-{b zT+k9}+Y@|=($dJK^2&L*gwhX#U%cc@F{7$>cMA=EW5x>yh@k3hB}gT z($6I|Ah+v&Ymrzo&b@O!3!z*nZmUd5XI1y;8*hIB$}Cq2-P1s?zZ1#bZpF8zO%h_{ zNauf*g;s8dsCquv&%GWNxPk{tJo}0bzw6EMKf}9`nDm)x6B(P#qC+U{KKRG3PuOnC z8f+``TN=Z49Ys7@(=cUmXW0k}vlRX+J_rT!<@P6~bOwprwOJuNt%6E}a}ILHZhCwR zlo;Yfg0mA2e(RG4WAbtZ#RFd;o&2I8lPRnytVbIROI8e)-qCI9WYD2 zZ$nM;TkhJI-^8G9zKEHhdokzoQ4kN_!TMK1l90A-!27d&h8DE#d~A3OO1I?*itGl- zx~!+&=j_&_GudHe1>?lQjSYpw$0-_%sF$L$j6IoA_2OlQ>6l+;8N1pUFodS^*baAq zJbPy)q**hIk{CJ;LVR+h{cFu1={uK{2jRAUkeC}?@j5#I&S5#IlN2WuziiX#jp7_! zLz)K~F@W1ZLqgr2^+R~%Ip|3VBgKba^sApaRu~s&p})9G#thDL1a++&RIhCiT{&J= z*{my+!{ZKf?yrax`XQ2>mxv$ar2H(V8gXKCPPZ_iX`MAV=F_68;(BJg>@fg^lZS7E zidyL1D)jKXy|&yC1>BVHm^GF(YccdR_kD6b{sn%@9obJ{d!IDg1c^ZejBfokR6uU} zc+<6Vnp^2xa?%^pDc^`g#gql)S{LBuw_PlD7Nh8BS-)uS#OcdsC)w$m8d^_&wy(Yo zrB8BS?BcS}v)=zMK>lN>V8rQ50{Wex000rr$PDdH_-qM?e3d3qSLYbcKYTg73Gqgt zR(buyhwjP4Uhdg}RUVKhB?HwwwRc;o#DApc8*dV+lE-ghsAb;6*wDSAe5TTVhxkd-=a%873AMW^c`(J#k-U5)L|1De7 zJ#XtH&Au%p8h6~x7PhkPE`$-z{lUP|85C1$E}X0*|8-DbgAF`1jZCuR(_qJ8DKTf5 zlXT6?*pGzHqzskJ!FVaM`}d2t`(g{~#0@89D!VRizX6PgVK&h0LWT1!y3P z`fUHkAsd27!KsZ8~j2^0|z-e}DG%IgxHO=2mYUoruIk2pf3YpL8!;I-u72 zx;n0TVXxV_ur4XAjh1R^->JHHPoa>+0-T+9YxyAf>SA)8W(ixZ;8wZVe~|j|Z*6yN zeMTRUy{0t`98-LB#zytB1-iY2V*14nxPKje4_v%Z=MW|rd;Bw#W+Ko@wnX~Z-d?u8 zrtH|m@v0UpXa_h}Dz(oT=;xQ3yWTzlTB^0r2H=p%Yv&T_SJc4CWU~63f3I!7sW9c# zO*Bj$Lak*QNzg5;($n;AtLtEzKeoqacPU^(T`G1XkKLkzB3HCFo(H0$y|jp^fWB8J zT+=~2&*FG?R^q$}c#($;RuHSsnpyU$xVb9ru&0);lu0`qO*?A1GmtuU2AXZV6#LBC zD8*ZE#mZ}CM5Bzh+^*5D!-IVM8fRSnBcx7sr67Ov!Ait5dl;-9=>VVJz<8awg_Nw{ zxsln=aEPy4cfSJy+Xqm$Itayd-S;Q<^Tcs7o2xp+b**Jy+!^AY*N1rcEf^0V4~K{r zRC(-q{YJXbje8jNw82s>*FFV6$^vj40y_k4imo(>i0_K(70htbBZY5qP0y#;!P9Ts z1ny@{%yCxap=`C!!v~p{5seXm_?HiI9kSjlSGKPi2tvoQKd&>g*Os#{AjM88qWnWN z)Nm)pu?&#uJMLO?%6q>NNF2=d_dJu10B!Pxf{>{m`0@GJ;Om591e@L8VFD1TeGCye z0Td4-YsJ6t{5-#45enD3El}4neW#rC)T5nGHFOqS;N#m2#gY0f7Z&}n9?>?(9IH&M zZGCv|;v5XQww}7VI{C;;3OkFSIS-V8jzAdz0Pp zc+06MvqG^~v~MtNe%G!RS)xEW&Mu_VQE%6Y(mplU2?*)YD2Ci&q?ghbFjad=`4nPn zWHh%`-z2rd0m`TJ9x>`VSr-BY87CI{*n=<=#2o@LXu44TLaf>40$!86$V-9?m)HtaUMDsB=NsOs{IBQ zN)XiO0`=R6)bgO{ut3koiUon$Cu^R{7#Rp8hDLXI!sGGBqF85#pH#~x4xW(>Bw!S` z@^{{TWda@+slj(gPJE@nt6X5sCpB+Ol?q%iunM{`r51JB?Kb>0PezYNiBB441>;dS zPOjPI3?Zi+{9g9w_bT{(e{NhVVo_Ti>Ns57PMeFm9U01~R(8&I$)xr$EIqc)?`Oe2 zsg~Z{xLD)|G*Y63`_$RuJ#4++Rh|<=SR(p0N`D|>rD?*h6cfJa)qJN!l+lkF(1CVQS7np%lVLl=>dMj1%iW$bg@qyTa)$=res%Qp_+W0& z(~_5sH|&!Bxggx}IW+4GC#DI-N(l2vX3p@Ei8h29_VtTSXm>iWU%f91uT0P1?eANQ zBz|q(Tj>(e02O-?-dXa4s!J%>B^C)lQ>SLeDPTZYZhRaso5aKl9;j%FMegu@ydjbh zngntj>~kLNH@{mX2|Ab>C55Ddv!M?@*w3Z_lA)*nH?XvDdm1oEsK5mzRGs zVbmea|BD{XX@xyV?Y9R_20i2A7Y_miWUbQ}Fitw&^$Q^fZ^eI3wLZrg1}bJY_vm-S zM%}s8P(@J!`*LCmy5qgaeF+LPV@2d@Z0TPk0!$e9sNJ{tPT}ZJ5>GS(91~O&nvk=o zx`8rC1V}tF%;+{k_f8fSAX<2IATrBH42R@kO*OiZS$=)32Gg0|z&sineBp$Ny;TVc zpt!HTyY<6Rs2c82?A&IFi`nR>1tPFN`(Q6!weQwZiL!~izB*(oM*DJ<^zz>6Ym)t% zH;5{R5De*Sh4fbRL6I+xRZhfUbx|=4dDs@y^FDxZuQdleqMwp;MTS2Kzm?Ed(C$Ao zWYmS25PTTc$Bypx1^Asxg-2nkv8rz2RH?L67=Qz zLaqr46o=9zB{7`_Bub+Op&DEPB?#q2GY(L+brlWg{u*2pzMlKgcObaDc$7Tt{mgQ)xpy zDoCy+T@J2g0?A=(%a+ATO2=gGSv0fu>vWW-?MpzP7$t_bjSncOS?TlHjnJVCC1ZHU z&@nN6N32(|JWon6NuD$iDNwWSpRFD-i(q;X@1;uxl&KV&ge4J0{~X;(bLl)7U?hvF z!(lQ919F?}<|fh~<;V_UATExH#b`VQw~gc7*0z8P9ocF?6~&-8&bit7_ujkw-H>Fs zFow)xuB@_pxbKltnUZs$#7xJ*T(XzQs(AHpVVf<9yf%g6w3P)zOfD(=o=e7VdT>+= zpoN9V*W2_sHFI7W+*c>lX{dfUV=?Pt+16I=;j|7>yU7RU>WA_pFi=IJ-Pb4%!2*oR zqcQalmizE26emILD=;nCEeH7!;CSv#Ocf`=z)LLh2tU52NnnxO67rdTfNhl{C5oCv z5-00SLJ^HtviojF(&`6J8!Nd_gJ^Y~dF{m-B8#;sV+I#F-Y$I#dIYS6fCopk;dhe& zLmfF(hAp5NZopTSi`cQ4CzWdLh(sq;s92mX1#HAC(e$q^Jr;?>pd_R z)3>AHd+(Vb$bRoYdP~D{1q90PzJQG;n$U*b1?HD9oF5L`iFklx*y`&wssAgyMzM|~ z5RJWC{T`|&B;6d8g!7XW(x{pP5U``u)TH=1{3*$!kBughJ(yuo=90Pka694c;0 zINZpJ30;^V_?GG;bnFz}lxrn@4tFdOlw$SE@1sd_5_E`W*z}Mt`Q0xCu!2!P= zn0;HC5a5eFxtMbG_p;IB5AD zu3RuApMJnPHm!6~XJ_${KxZ@{|)AELG^q z1*1o3pEYtOAnfx~iHcB#nDWmk$hhQ_Qjwsubc;^p2h~R@d!%) z1TGarm4`Z5g$%Z1+Aj$%^H3lXMYkfo7Ocq$=m0T?&w`t^jCNZ7a__R}>oGCw_DC}5 z)Oa9;sY5+?iG0;W(-${h7+<>Bnt;HUWV7=+4xNIwh+oxD%bJAt4@ysxVMOWf`Z#3R zHYgsATwt4CVBTr!C5}YN%}?pMgWJ?u4W8oi47W8 z+H1!@hcw(1v6UjSTHF7-Shi}GvZIeG2Qc1^O3Zz^|Ek_R@c-ePoBZ)1$!JUFi<1Ka zW(yIeLiFJtdNDw$ZH1^uF&4@M^)!6X5}ok_T6)}g{0~=$^*>x4Cboao2>JW`@QHDh+jpkBl82cWe6QJ ziVw0nE*AeGpojNFHUP(VlRp~Ur+lZ z8&T>v!4V>vt&0(-uBawVNm+bj$^d)jtN0XGmpLrm91;| z&qs#k$&od_`8Q8XQ&~q|#V((5)z)eJX?Yk-aTE9tTb+OUUYPDKw2S|)-Mw604fW9f zAIjb-II}nU*8O7JHaoUDwr$(CZQHi(q+{D=$LiS0&Z)C+PVHaS{-67GvueGy=6uJT zV?3bo7No={$Qc!sDAePQC^b(Q1iYp5*dxA?{97=9E%DG}NS z)y?J-@hOqQR079aKVf4wcQ40!>}_^_0vp3B+mBZU%W5*)^XBt((cx0qx@mvP1Xsyi zYb?6$%*z2*1#YW6ihgQW1g<7haB?@3o%G=2v1S9%U84t753kFSVLvjvmHv)m_z)Iv zQ*GsqTX~wY!>aY$sDjdI24vt$Yx!!i?mBr?2`k%INk*gF^kD1xn`H|F6Wv=sKPJ~Q z9@q2oXDdbH$7P3FSgy&HSz;DklZTJ$W>YDTi%$mxMSl&p9DtkYG(UiLy$#-#{IYR< z$s~>Go}x$D!!U>V*3Pz80F>iq`N8?~(nH-}lNv1T#N$uFU{RA4b8ffYqZT<^*mi7r z?PUai(5+MzFyk}T<$9`(M}#qW*sfjH;3r!>{`ie2M@ONK(uMzgOO`Ok!!{b;yH%p?-m)?mL^o9{*g z>vlSrP-28PkOu~d~w;?Hd6M*f=^ z&KBT95Vq!rq?A^MjcJgm?bD@~!^I9chp4~1w`Jyo(mdM_&CvmC>uU!ln!Si z4H1Hr_Sz}~1jQ7`Dn*sJWg(aixSTG~NvT^gf!H%g8m@pC*e=BCeuv69#g24LIE7w&#_&G6@A<5hnDg!mMC zL)xQWHh>WR*hH&71ju|ppz~0C9Z%TRe&u2ZvHuZh7)Uo8qI3F3Xa+d3Ar{J%pi*OF z#o46(`ptS~wYA;5E(CwzQM)hb38u!NEu+FW1EMMrqfzRQ7m}xQ`43*GVw)tA2A>Q2 zs~KPwxm|EM;QjVgygZL~0=9DD|+>MHb^cc#*mRz&lw3oFO?1L3|MG&dQFwORM+j>In$1hn@d#DGf<6z ztDgmZ7te9JD5Y-jL>yb)gc>;lBy%ySY@2(_aT`EXz2{G!VnI1*+sse{5Y35*d=mEQ z>OVt7&Zr+XdGU4#HTl1Awhg=So z)|cLCUEuCHs?d1Zax)-@`5jIt`oj9}gyAM)C=NO;`?|=5_@TV^rj|++Q{ApgdV_Rb8c79z(2@HWOfs|p*WSB z3u0-uDCow8zc&7JR* zAxvba@9)A|j|1T+mxuUQSf0K~PmZ}>Z6_VnbCS2M*G~{bJ!d}#ZVxtjJhJek%7OF7 z%h=L6^F@^c*P(GZ=6UHnX)`=bbdI3-(3y6DEeJ_RuRFGn~19T;ge5agZ@za=(vppXeS=LqZx%g(1)o;OKd_qM$p<>&K+etKcQl@w7p-=%v4*_r!BX_`i_zy?*>RUwsTj@@1>&sMcXVK z;G;0Rc-yY+rgtUcD8)zynf_tHg#kGF5Z}&Tv*<#t!#mHMI`5$m4*kLCzgx~GJ?u?p z+Y1_rC8nk72jPdGu>J<8Kx>*pKfFYd$|%OOZU)&j8PW7_#7yvaYgZU~PV1A67|1MZ z`p^LfCq&>bds&eET5c+_#QujgIqFLYGiLRKpxypdO%E|t{8t)C>FEPsN~@WdEk>}v zk`zVk?4Qp5@AEhda^K7YgNgcoe`e!NO>WbmgE)b;!_jy9qOE0I9QkICA28|l#8*w} zW!0kh9v<8z2D1m#*Dvtk<%yS6yFEuQ@%;jNs-Bi-%$#GdTo%EVqDN>?--wTkm!- zxn_O+R#-WY6aN@{4}_b+(-lJ;F-;@X$Ur}g;G(v@>O6Zuu+;OX=C-1U+LM<_x+1Ji zMK&651+bU&WrOHG;rZf*XuRx=xp{|c{GumHFR_-5yOT%$i<-b%cRi5Xd*SL-VED|V zu;Nf=^U@<5ja!m)_Ve(Yz7K^J@;^FMw*OMd8JO7rryXjIhKAGD2$Jtf-I`c^B#Z&P zw(Jp6K}kB*x(YcKC!GcYA;3~$2W$nP%96vE*9(r(5Sg-oa(sGrc6R;RS%zfIYa2Mt zNB8CHcY7Wa!AAXWkx(V;eN?8Jy`jZde{kBT!Q-ZQZDSy(JK1Zws-&LYI^(e z39q&0xvti_&2CaIjq7%zXKwp5hGN%3PtUjg`=pAUI@{KGaBbAI@7+8|7$ZQub-nYo z2I_-DI-`=7PgO^+_3LP*wBY%esX=#fK-6GQXLoyNXWO2;D$3!X;^@xfG#Y|(+{cw0 z8N2NA{=Xex6z{!j`hdnGT~#$azi-OT&=oChH3VF)eT8M6-Q2rgOG}Mg<*Vnd$}D_2 z@Uji+!!T9ZyCv1%wJQay>@O|E$guu%YFh_RecEl4;Y86yLY9BmD}LeOoquImWQpxx zytt?++$lqiGN)-BrFiUi=rHfQmGfT5$2jAt@A9hH!XP3)q@T3EIahS6hFT@`MZhby znIS~Gg5dF(+ch)S!X3b#o>(o&{SO| z6JFS$@P=>tu~F8hLRaSF^(&Zt_pH&@tZR1upzjs`_-zo|og4nVdk(d75Xd zAJge%QE)yyi8q(k0?;KIkzL>g1Db_b{0?lYx^5E+Z+LRXczzP{R?`Pxd&*i*Dq84U z<5?(p_;g>(nVc@DZx$^4P!7M|Rpy`oO5~y<>+o3k-x*{c`G=E0@R#=@BKh)DAtzEH zH41~qI4Huth?Qxr4Q$z6FtlGiQ*vt|RY;>1Zc5Ar`7mtv)xR_nGWTFcnZDvV#VdK} z=W}z!+tw&Zyy3j1(``H6F1PRhB}HDB6>D@!K!V>iKX}7K zFpk@ESc^WA?O*#V-F)o8$j6rimfcm6@i)E2CnfJmBg))RsZ)`GSAmkLFX^GA8Yrdq zrAq~yknWB7%l3m58~4i~kdQ4J67)n@Q;A>7pX^06jO#|rJV*TVck|00|30v`pc?s; zaY3*Om&&Oun2rEHahf=5iLdq3J2*vdh4hf%F`gMpo2qhqOiJETr9=jc1M-5+o6~XJy7L&{MsKUGzrU1>1oPmCY zIdjmzOG{jE-iu80Lj9dhQ`l;vfSCSfgSADdFb(@RTt_`hiT*#@i|iY-eo=YQA>zra zZO?)HPY&qW2>apQM*J@u!CRHoH%Bw#i?Z7-TJ;)KjAP3X{l-ewXfL=~?#Tm7e54-7 zf5FA#9(*;w*dJ9G+8c4nC3{+xsxo6rE1psaRYtiZOpWHNgE zS*q!aJ{g#tLUNE4H29e2r(W{&bz100hk}GVG?K_dm-RC`cmcu;9ehT*DD-Sba$W@p zByS8#KGo0#EZxe^JZg}TjBgv94uxZ@MvYWE;Nu+xWUjtZR8G7t>6svtq%O7?L7PB4 zO7Mit^gRf12?K}Wgy59WQ`*)tqrnc2Qkdjh^j`aVlh(cT%=;hmj{}vJCTFthj=qp2 za@o`r6;u84P4r)4f&mk?!t2=}N~N!xP)_}Jp~)xLXtp3~SDwqT{yZC*X*2fMj{0{T z@!uv4kK5Dr)0G`4?y;1gCcNw;hI;BFO?^hvm!nYp(Gs7e4v0#4l9CdA57zMRkGe*( zu&1ksOAkkvy>&kk?g0$w5Z;n=?!djM0)lwn zNROcUNzoAxthe#$!n!JH4#9@yo9y zN9iKWw82TMdkd{d=wFNLuw{ZQhNUgXC^m9f8dEU!1Iqok`VM=p7bXrU@Dr^(3~S!!YnR()2`01*Z2MH(~ZENC4d1b zpU#?EV-dYwBp*vsOK8vg`F{KJ;wB;|Iu~_c;J{VL1pe{O;oi;gUvsaX20VI6m;I_qydU(nMkx3bM^^C?86BHQs7}d!r!oU z{()xHy6dpZo%eQdcp)>N?M5G2p7`U+1Z&i@Dyui#9|G-$k}PI3|9CH@XDekT z%+pzU*{U>pc~x4uLY(rnP6udd^c-hsz_eO6Jz*iBdg;hr)~~Cn6Aqz!62@E_@1?u^ zR+Vu+=Q-gnxO~YPkG#zt4B6u2c2ycB?#;O5?C95Cn}b>dMCJ836WC%ZbWX`c&dlT!TR)*V74 zCZ&>8yQFlV*#6k>R0})Iwc_aG#3>-cQH6xy0HQ*s$oNoNW^2(puUmTk8-hP0+>0$3XwXXdzaFnx@6{`Zg|wR-SPI zF0TDrMeSHdNB{Rp_W0ij9_?KVld;#*1{ew)-Odq}8GCMWz_Ks+tP3R+ptZ<_W0fSk zi%(vhd!Pe<4>uHSrlqd~es;hE@ofbD2q-Wfjb)XebtSt!Jy4rMa^N~YK6coBK(=+) z4&!BVF7}%sMF*8}0@epdS2)mg&9GTw|I4r+5ElEwMc+Q{A3+Pu_#sP(tjZal%~)eO z+KsD@ES$+7eVc>m>U2zxgxp$wew9ft1BZzK5izYViHWMzpD#3#{_BlnXrUiqR7A6F zcZAE(zYk#tqz4o#5sv?CdxlDGffFF1`5Po>t^HT0W2$CjgDQ1t@>fkhnJRWM3%AhZ z8k*!4EPXj$b0|i-EoV1_YXHVb=eMc%BqnpHU@ablP$P}kby<|qAIV&%yPK4S(6NwG z^GfGSx>R{n0fPPN~=(Rv-Wu+VdK+QQ9N-D|9OlzyBFlCf%QpA3g^K(I^@ z!%n7B>!xZoTKzU$cqySzTYb^Rho)rTpiI|>sgmXpHdEs!`?u|NcT=AN;i5tzN*xXd z9j4wtNC>}VLBT%w-LlGEl>x!8_&#G+Qj3ql05LbF-?^^CnfFcaAEUO;U9+!Pyu2$q z)6uo59eF>g-t0|t#+R6+OdsnhM|Tv1oJW{K=#IIcRfxrhTC(`MRwZ_rc4iuDmnqCc zkj00X0fUOx**w!T!;nUw_B8cAUv=qlMIPz=BT;S}q|()BYr4C79~dw%HA2N-wXN3>w~X zU*k$!mLr=h97qTVY)6VzmHRPdzgIm!tJ(2E_aG~zi*=}P^VKSLNFM$Sf_ggX)&77o z>3(~DkG2u0FpVZ!pqmnd5W}C{b14a^#l>jYB`fw%%RTh;biCKUMjvv#gt2}eCwb^j z(lD?dZub-o&oU*ColqN&(+zL9$Ks%cRLn%&)Psa-mI!pES}?GQzZ>)j!2hOoj(e!l zh{!Xp)Sf~JdR%se>bF{@+syMR3+X+GWo`sC zH0dKK0-h(J*xa#BClx^y4Z;xP;KX(y(azInUEaI0{%mdU>h696js49?5F8BK-aZygTVPLKwHPS8UvKd2bX~pna zc=^oD7T7>5qeoEn6mcxk4Rq|h%S_xX-guC>?P3JSF2B%h5@pa0|2Q7+CnD&^!83<_ zj?s4MwWe#mih~4;2D?2bJs^pwI+jt-xMQofDm*!kQw3`5oT|(e%Bm-}XbVS+U)f2{ zah|FV29&#sJ>sIWutb0}x_Q{jmis0R(qu!@RAt^`bQ5(llh1@y3NXBRFNHkYG75$? z`n~_rEKU)33=yG9n@90z6zW33M)&{^)=iwjA}AN z5_nhJfQd`p`$2X2X~*v7IxSEK@EmEX=Vo3z|- zz*dEsES{@WcU}7>E1`I%f`1$#Ezh2-`m=2QSNx9ottL7U+`10ap4gG6!sKO8Q%JsX zIaRflBgAO{>na#fD`FI`-~OQ;K->Zo(hjlgUj-(N@4v!Q#s7}@SK4R;$cXYUUAkd2 zdO%4GSQEe@7S;haJ*xrzNmU$u_1{7_X9UBlHsln1KC-mEcs|5m@S+-{wj+`~&w2F6 zc~x{+*%E(w^!EZ!*C?uY3jLJPR&$U;LXa3>+xzoEmbslbfRI803+_)cw1& z7h_xsi?6=T;mgrBG3spCZcfEB3w6~&#Z~pLi=x}SZN%1xHB?Sa1m99={~YG4Jf?+R z6vsp1ekTUpzRW%@h8rjP(CEf_f$0m$yd9+9P+~3HY;E?S4m}-Eyg0Kt7FiQf%VBse zlB?@%>25-%l;jdIl*$iD1l~vb%-vN>Z?ux4h(zUpIs^kVdlyQubZ~$p;U6BIKO zsVrZ{9t~$PRT|C5Gmh@l(!5`j-*Grxivf%qg{)GNZUk|`g;;yI0@=+0x!OjoX*~uB z8%s$l>Fh~AT48pab(_bUfc6Meolk8~_}myyyl5MFfD=#OnbZ?Zh6)nZEAuK_%m9`t z%E9rps*J*kNufAnSeThx2Z|c`(OsY6oJ7gG8{q$LFFBS?7XfNcKa?gkBf{2KE!V5z zeP#imirIcz9xS051_>dQGg|E!fC=r2J<9m&j_cE^!Euq?$BI5v;$($c$XL5R-?)x< zZhai^GUm?Gav$3S&|awgBc0Ea+Tj`IBqV@DKh@82!JS-i)dJz(vL z->f|VJm_jn_8WA&&EaYLQD`2%dGUypvq=gQ1C`pW3WEYfJZPdKERs&PmqCW~P~#Fa z1m9Q|k%J=gpXFt~C6pp`G2@gvVut>&NJO&{mjmf3Xk#Y!FKSMhB zIVc73R8FcAR(7L*5`eI{^Fh1olve@uqveQobD5;9GWiC(SGkqgr4T3q%r<)Fg7W=- z0;Mh1RonbPScql`6GrTJoPWI8Si3xNZ4JY7dvP`Y?wG>_YRY}Z6O&mEw{CSOKPUOr+0H7j4vCLIM^FtF(4+VDk zcL(*#m2+U!a1E_tT>815V>G%x7~rNZqJxsIX|~PQH6acHo>HRvUsDuS!2a|YxIqP0 z8~~<^A|*P~YzN}M+8}KB?T(;($-F&W=<$xKRG_v6zH3l ziE(q|GK>%JhWUG6xtWn(+H|Ksa%s+q@if0Wsf;TUX*o(_jWBK{N5YoBb6juC#5PD` zW|0BBDaD?E&vq?ZdCR#@`4A(MxXZtSK2fCDF)M?A_|>TRLwa+D4dsZna^YH+2VGBB zVpOUY>19YRHhVE~KJsXh(MR&Y<|RyFW*)(!nV`U+i(E4xT8SLENaY;+8(zByQjidFmv*_t~(yZJWW@HIUm$q0FWq;8%D&An{@lH)UkgY3})RgCQSJF->vafUYqZKD{mc6p~Zx1JKEsIw4Q z%~J-Y@3@qnW|x$Z^=Aof?DxcyQZ8vGj<6}Af_C~a`1H{|_aCFNQh*?Y^fpg~GAz7A z3mylD>)!G2o(qHaz&wp$aS_mQTu%J`$mn2#s&twkCZJo^PZ4mm4r1O>Lwj&s#Fu z_EHGdTtt`#5~-%ZdM{4_{+X}Qoqo5o+Mmu4i@@ zdk+|hAiY93mQp?#4r;jMX@^M;?(ZrPiub}W2q5+>bP4R|lW{x@_*D*>g$fLui5I6q z$6u|d9Y|gD)vQN2fr^a&-!9$QXv{d^dml0ajTy4BO}(dzGKl7^MGm0xAg+@ik^Ivytwo(Qp~5XdufUR@k;pvvNCK>1_~d8*3vBi`u8Zd<4vm=zI5j3{1Td=K#RD zh3~{0)*8^1{fXkxktk!x@eZ+282RP>+&n{^K|<`$)S}Uh?Ol(LE^U7N5Mi9&Sxe!% zOJuT+<(++-yRZ91{SqG!AnP{S}uWT+^LQSNW5*tZKW|E>{5yDm0Y1TV=K`PDYy*{-f(JuWi`dl$0LT zIgN+!rC2M22!g{*HGzSgX@25h@54(o<$i_LWzN0k%66Yo|%DB1yVvGe1(B&F&ggn=P5QJRTE>|0y6`1AQsSS zO6kP`brQZZ7Iqb-)wMPC!9{362oG@Xi_m~Ia_oZP3Xx9U3X@DxbnFUFd6)iJ#Bm0a z-z4$BwZY}n3KnaLv;ooG-9o*+|Hd~-uX7A0OB)|XA&tOc-8dZS3x>BJ z6wJ1Jq7aSIkcasH!io&EVx(~ZC^!s*GA90mX-cS;OB}~)3TNNpl>P;@SOvOrM`R@E zFfzL5j1G$#1dFEV3Nqt9G#ACJi(x*3Rd;g07*5V%KCL1BFP+5mwCdO2XaPg>s46=D zKZqdUVB{sI5Zc*Lph$G?#ZHc@%ga-NZNbj|uf?YT7~}2xma+77^ zcnPo-$Wp=^XM9Ff=bHGR1R8QiOky82IdLD{NFvpB$e+;OMCN22{PM44r2+bJd7|Yc7ON82<&C>Qc@+TbcmDz zOodV-eB?ni&@bFb+Nz39azI$f8>G`PGPq(zstULt=+uLaqIe}=+4sMwLMp;$LkzqBuDsbi z`E)a-n7!YR2VnW4|Ikf;OZmJ!`$j{o=8XjHThVam(qYK=X1X(Yv+GXb9~a#gN|G$g z{fXg|t<{_E&6Ls!?02Wyho01)y`@`_ulTz{b6ii*V`g;$&YQKf9=*j96vt%r;>eX_ zP&r9$S4oE>r8)~S_>90ldH%IJ{&3U(Z`u0Gfv2wYNo_DMln+5qkJ|TIG6k#urz*QZ zx)?M5D<&-Q)Y_Yk&xv6ax;6g|lO1rFI6@l0(T*%}jFm*iwyqyaKb96U}3md|Qi3UC@() zBDLpwcqWxlD~BtSX7p*&kulfz&A`ElIkWoxi6z$HzFC9W&%vJ6byeNE|7nm;{jz^^ z`PJL!5ALkZR&jcZz@};<@qUG5u^_)O*~0G_^0Q&`-CV(wNWfo369t_tB~_fQ zX}59&m|oUY=`=xZz6jqr7nzBii2^w#6+L%7QMGv-DDAgETf1L+o{t8ND2blq8B#hh zX=17>dI(dbb|XH^^tdVg`}SgZr}UMATGD#b8v9>GLFZOU564s_U$Iq+mKXyyemA(y zg=)FT8|>EM$F7DXvg96fNNfJT7bCgN5Q8JSq?gZr8xMX^S6_AGeRaCklLad9zr&YH zZ%;3^rN*psuP&!dQ&i!r7KUD@4V!S~p;A0Ug22JiWX)4xqPKZQ;Y;3P&%K+%#f;R; z+X&}Xt$rppCSCiLaE>^&E?9^(qFM|}ahu_dWnyossR(J|(6;BT+Y7x_tr1t>|{!4zwPiHO9*g%*%XX4g;7Ak+G z&9Wg{bymH;q&a);TizV=d>Uj!al~=dTq+`H3^=^@c69qRS^B5O*{4^MD_N}WXF03@ z(~Qpq#cC8~xGF6O5ubiex-xh}NRigaow={OfWVF=)4?nZ@h@~jw1S##g1t)FE8N32 zF`PRo9DxvNRYJI0McF(hDz|EI`EcBI>|{|lKbd^}g}>U5m1~PIm|Bp!wHJFpJn{5Aj^$MgEu)yMf1 zkwez;Y>H%&Y{q&4A$jT(C!I))kQbH!w7lO`u7y=ux_?j1LJ*^p6T!AggQSl$d@38L z=sb&6vHNSMXh_@uVy9^gb~6ZMjBxR7IKi#}ddS3v$+T&eaNHcXEZ|J!*0RQEhI#_O zgdR^LqOA)Et8sj5y&tH{;9LQDG$*#$L_9XO93?;d)q!giZ7}&TY~7vcVU`FlwN8L0g8^Lr;}}5 zfQ4xQ|3iebd>R}nNA%72JaLgYvz?1DiM(`|B{;{N8@H_kDn)dV?t|SZ|2VXf z!1qx=;e|FvraKEyAh2INbN3vk-G*^Kq%z(crMrTue?5Xn8g=Ca0YPUVxWM>s62y}& z!4F>uu)#W4j$%6%uz^wF>;;f{4HuLhz&zy6{s!gC7fiZ8a%%7XN1X;QT8Hy!T7zHw&?|Vx2LlNr%VSeDaXMC z3o;-=c%>m!L3zgD2$C$Q;@W(j!QZbmJgIyGzXcS7kVZ$V!(D_s_gLIb+%%-M3fd7f z7w_-5z^A=w-|GMbV&Mu^sF#H;b>tT~QkX?EL}<(~vLq9Gw1$}i?haqKslO2-2RocF zVyd<>OoGGJf-4!gYBO5l7$i`#@x^k4ICb@0cmZ2zpBDM=aRsSn{NHx&C4xPw(=pUd z`1;777Hl%QA+x%wu=U-}Ga*5ot^n|w>^wLntVq@q#s~kK7*c7iPFz0%4AcdrJas!x=+$sZF-6h zr#c;1)}6mnb;m}wKYXTIxM*~+o99ZkBI~wMsA_inibq%$&KeFZ*yV0`zqtGKBiW{w zEq#_#7^*Qj$3H`D$siPtVnmsIcDDhoph%Y;;UDB#Ovow!gb5S)_aiYy+08fF+5_t> z)8oH2+Ia#e4lF2j`o$B0?X!-6f0XT5s|Q)lqs%7?R`{*5p84Kx0tzbepI|U4KH0q{ z!Z;nQ7r0MKMM~Oqbc(j1N`4o4W7`Em(q-xaj>;11`~JjRZ(cVYS)4xmq%w7hg^+pY zV%Q@0U|KNze_EC2JQ{S?ULdqFXYIzc#72BQ3b(w{j(^kZpw&8I^mZIJ>dWB*7vP{d zy$yFJ=a^54kh_u5g2A;I>D^6paFNdGB0rWd{i)_3)<)t4R_fzsWL1 z6of@IbiAanmG`g$%?Rq^xU(r4c?kNry zd%s=(Oi;76w`Pcg+taVeL)=-^WUKYPZCSd)RJ~o{9PKZDWpaVA$}77bI`$Ht_rBre z`mMAFR3B8jpINgc%9=Jzg{XpGK(2Lq?r`*_COH8^T(0d0R7ZW3{aLV^`R+(1$-k*D@0XTm6-PTyJKjwNtf&#m}7#e zI6UXi?G|)q1S*@)>?WkHzE3VFnRBuQ#fc(y0m1l`2Ige`X#B(C^=w-VDM#{_ z;}#{=ANkho4#Ogsqd2 z8JheooiDqzrW+J{-vTeY+2#7)V-WJ__n z%Q?2^@MKTeTOh6xmz`$`x+dRLq;PmtRx9GCZNhUdt8GF%L2Q4ko#iZ7ouh7*2tC#k z_JE#?sa+^;4aY2|_fdbsu=g2{%ZSK4khf23!+2b}4g<9PVA7RoDdA97YbOf_@q~df zbd0_l7;SZ!yN%|ijyV@iTB$9%Y&DbXvNpOPDeKGp-_*TN$j)!qj;9W%5DML=GAIrp z8|0@hJI7e;S3%cYI3; z_r;{elp77OH2_W_>C=zAtR@>$O0*7w-I0@zxu3Ast{JbQu~B zz9<=Q`dfz4q!k4({=2wGXaY3+CI}y9ouRRCt`xiRP7yId=!BMmlzpFnC%fYnSeyyd z!MHbw@Y)Eke)7nE)6D_9ZMH$aAouuboD?}PZUqEG5rGc#d3@|Pe7+vq#_h(PYODE;?GD8r2`B|l{qeP0u~}UqWC3FvCH60X^VxIv2{hx zH8Txav0&$5sAR#8yF2JZ0dk}T3p@#-fF&?*>BxqLOXYLHxnit7(}B(t>^T=|Khd&s zom|F!04Nxy{IneZ0G6vT`E+}Q`#fw+CpJQ=XOY7QKxWmf$Ylv^|6O2XWIt>FhQ^%b zS`RK0>u<|7#wh?DcaBDMwja{~@YK0^5Zsgz^!@jdk#uZ#!7%KW=225)-61yQ!PWnJ z`n~I$k%H02|3#-N&!vK`67$5xw!UGxXJUU?NU;bBMsh8$0PSfTC{d+Aniu+vi#{xn zhoMi%5ll8HBv9GxA$*O5bkfN(W!}Zo#lh17_Ua55d*9a@va5#E@6u(KRPsJ{OAhHW=czSx!l?SZ~_GLa%`OL*^Wj<*RCCYq8M-5rA%g6 zv)#V2B|h?7y#@ZUT~?Sz^z>l0*JQBR0suev0bx)JVHBep=~eTB0AJk2^&D4UB{5_K z(X};vlSDi|BxQadJS&RT3IYR<9Zpo9b)DHLNCJ~B{TE^8h765$*M8~t+I9GgDfumz&;r!oH z>;GK~k5Sc#+hT+D-PJRAD9JS(ZGhK9@EL2MQUnFd+%|?G1v+nOVW#7`8ydy`?)}*| zh}qZ}#Xw;Yo_=yv`Vy3!{qTINDZ zObW#PM+cKe{JsMlD%~J%wrN(F9Vfmtou*E?h@*v->S;Yfs}KgDi(qvA*mYH!yl!Y} zK9$j+Ka`}T!3b0|SYG!dzaRpJZ7pH(0;N%UvN<&54-t}|k5(-pDM3PmC&pHd+^->N z(1a1bglMt|#X4Ix;>;xAmk#I^H*KDkpSJ+IULJuL=7w-VCny4U^K2asEUAg`Z-FfP zA*I8{`?7Si!pUOE{<^?EAF3Set%l9IFy~ut?zIv{e?z9Y)w`+tdRev$5HibuXkC~7 zeWEN1+&tu0%5Y1SoIP-Z{o&HKDHktMuDkBLdO!F*_G2-%HGLGuf5u_T7fBR}f*qpO z!HobGcN_EbZ(M3-b$!@PX^Zs5E>V0q3_{#QA#1nsR69( zUu5WTq#gXR^9zL@(%_Ju6FEbn)js-n0{{^gJF4S)u7Yc}%`BbkG}Vuw7d+Fm^(z!< znxb-GYrU8ruRtl90D@YPT*wOubiFZ4DSb8*0`tIe@FXv9^ciZG!r(ELCZoHVd9Gj@ z%D9Jl0DN_11Nn@x9xL}hMCJp*x&jB+v_LRT5Qnb@o+1^u`mo!ydh^~_Jn*O6DXrfj z#dnw;s2;J@^IM$lHrK2J`etMb$TsYkcvMVp(mZn&!x~PoO?&k|zt4{kHbVeR@WS$_ z2~*01BKX4&_{v>TvwDa5p}!dj&_u9#xW;wz`_XdD^Edb24&*v(3$7x|#Z4l&4`w4# z{&C*6;g6(QVTMFN7Jy`}Jd)UNEHL99BF z(2m-SC-b4GX*-oKicdo9DpIc1oN8wyKoQ!o*Cz<^#`FB52F`ctkHOqy zun$P9bh?e++%efQP=|r@)i0X_1~0?Qp0-0lE%FInnd3n84tSvo=5iZjJ}g3EpW=ZB z=5!tfICSnCtrG8xlZl;kfK)Bcx?b4&dC;XIgR%ZH_tx&?#B*irgu&F0Gq}ht+({6W z_(Rgh54jCYdy*lW#!BeH&fMeaS(rQZ=thUAxatI?M zsg3W>fUZJPt}iMZsV_q-Pwe*x8!>(3LaJ8!3asjsvEWb%3k5!eA&Sx%*o&?hpL_P9YWfY?nQL! z$S|LQ%OGi}C+(2Am@rIjT56%4J&x2gBEBq*-g@8Z;wXzfZ`Y^C$Kmr9%{w>O_Lfy- zlkC-LMrFsrXZHmREb}kNNjT-J!q6}F2xP!gYeyM`{7fBg`^6Q*t$?i$K#{Yr>b%y3 zH|#>Rq6Qt>nnkNS&Jq9bsN43Lkn9NXH8~dlpoqxKtc?clSzpf!Jq`Y%`+t>+|7hHq z*qE69|G1tG*^qsoe-JY&tj6|1*)=}t(+wp{#z`%|lWH=jKxpULs+`?LsFIAoKYU}r zsT51=8eRnJ=lKcDS$e^T|C$n$fAhSL9<8(UiE}3q{}LvmNLxjzBi~7v#^mnMmgXhX zl;+d>dYMMpcFmqvNtk=fUfj~r#pgI^u}J-FNry3L@8jBpY$b$njK-CMz0idBjp3JA_~ zvZ9y?nwM<-qg%r(@v7?GTi3Nfm&D5#V}E39nu8C$%XZhRPRnqvY}mq7&q)y3vr~7% z{hJsI=dYfe!J;!dqN@07o9~;T=;fM=X z2yYP0bp=Myheoz$s#~yJ{Zg%99el$pWL(71;)dwY=}V~{L=%rjh_<0ct^{_<-|WPd zY;IST=*OjFNs4LLSU4KTtnT;guJyWPC1Id%7H*cB=keJ;#Z7s>k}U@v4f32v2($%i zGfr(JC9>zCJJeLGA3nS(isS@286{bdcXHR&2vkuWu5Cwjv05~lsj7?>6?Q8YkE6Ak z^p#-G5-GaNU6c*1Q5|kLfcS>U}>=Y7& zB`)<0E@wP=KVSF{OBx&2+#;dYHruT zGNC5Soc7vy>bZ7K^Kv}WFwTc`&bO!iWGQ-swKtx`betdAh&e%U*f?j(rQvdkL7UlORmN@bC+aVrEC(9^K95 z6|frBy+oSCTZ(2-R*HF_V6S2m2UH#?N)?UfYnX9xm2or1zY(Fmo%Cr>`ebRmi$a94 z+raO00b1?93J!p-yS8mKj$$`HN`CJ!+T(_tO{m#CSyv>uc4&O}dy`Yj+-MeCvP}RC zk|s7r=g^q*VF;yr!#(tyy^#Gu5#-p8yG9btF#rNPkkp# z0x4qYM+(f^+R9Ik5{YHWC(gY_s>=}ApvQiOmikHzl{aw{VqQWo(J&s-KsE6&co!vN z$!32zexu3v4eGRj6!7~VdEx&mkRqYhC@i3+ey>X;S$;VudW{_b%UR#Si}XG}y|CUm z1=|5Pzi$;5g-#=WrB`CvuBp)DJGrrilaul%D0r^h=N^nj%f5uWtXeT)byboYbLoaV z6V}a35RLcE0j$ZxzKsA-;VnE>0YR?~%8KZb|(%J$GJ~Q@k457On>1?+aNC5knM4I~|b?U6DT+*!Q z>r))95soPaFF}@g8_kRzi-|kr;A394J!a>Q!B1Hz5GXc}s!}Sxf2}2lLg`YMw z;+zMe1FPbAv0nsezW9TH!RVL6iKjySnVK@&X(~iVBP(%5E7rR@0Eo=*#6U=K4qWa{ zN3P>?PVjAo&T?!RY(zEi>LxRV_Op+Qg2zx%&em8|4rjdXQ|KO3I^H6Af$(zsxDjh} zY>?La07$vSzt&G~2Q)@fXDI;YI%FEUro)xvM-_$D- z*0DVpoyBv+xe2{ey;+j^wXKK|C(Dblt(nUo70+3|Z6#Y_(9S1ZTYEm!`+50??(XBZ zWP7Rj-3{ToqB(U#$DwC`TK}*DgGmFR}tTX|^jXNBAuM&d$dxH#Rn`l8sy z`hGNmP69m+;`E3s@|2n4vWi|2iiWB>wa}itl&BMbvjW$j1dSxb1z}*FQHO^x{785P z1M>a)KI&yGBE(YfvC;;p0q9-&w5+by;8NtL!>S<7k7iI2^g;3b!jjqo=O%Oy*R=uj zur=kiJk63HHSb#t<$e*w*nSB9JcChis-%B02UWa{`9IN+w$=r15QIYeQ|q1EG;bvY ztQ4P!tt6{!A_yoB!74m12FTN!A(v++Kf>!lBa{w|j?XS}ZqIZDy zFmUiGN&iE2(myk%@|lwfumEg7Ob(#aM>W0DyAjU96qyq#el_vyEX zI~YCN`klVDHpHAX9z=;BV*4ihO=h}ThkiLe(gN~MDa#XA6@N^m%OB6vZo9hLKQx{b z#<{=hsIo5GKbaFewdoI9J0@n|l$g2Re11MQUivR-=7*W6@AD*}v0eHKO{JEf%p+OM zBN;2~j!i+^HcR{m2ou;*5|$Tt=ff+uLTcFmHYu1|#Pf>STYSc=RjP5-me{^Q&MF7Y?$&STe+4cbyET#tmHd$->uxrY&f-L0`j{6xMU#9a-UVXWiGYH{BXAn&gRKP8G#x6HN^Q zT=>mo`>IAe3~3{sK%GO?Xf;;b|4>!-k5)d3_Ng2d?g)Y{*|L&fz${iRWSRSzYO^o= ztyswtQgzvBxha;!rNKog;VB7`Va{MN|8N-p<3OHbCcG^}<~G&q3J9Ggj9exBgi3=6 zNqlacr8*!4)bpN{|8nISi`+a<_HOj+==o>86=PH|knh9QhzLb;xU_M-J7ZBeK7Hrg zlST8qUqa9|93f)~75B{8)cfQ@RdUGC%+DBl34x7<4@4Awb>`h9>qBCS4LO?B#^`KV ztue3W`J4SLbM9<8ov*Z>e{4n)w+~&Y(@@~-gU|g+@RWehkq7&fchp5ITPaE&A{xQ?Z`?r@8ns&y&KkVQE90_}e zfYZR3iCXLyZ)Gw9P#QbUt%&Sl&~*Ixmh`g$qXvcx6`sQMu&4~won)a^AlV>vQD|=aJoJ}FHTv>ItehMcOv~>XiOSjr zudYw@VYQ2Y$WZe=n1_M&QcIlo_l$>o0o_wquA5}pv?t@PEf-+`w<6>kvV`oZ^0TQX z^Ko7{-PW71YiZ*39P{6*0S&31SIE(OQt+A|=Ds6#Jv+Xr-grT}*}Kiz^04v^Ei7#S zUCP66W*z><>v6}j|l421)t61(v>bHy5!GGhO}Uek1TXn!$H*5^<))F_@zD%kx`CDR-|5>-6}56pe{&YD;9{tS^VF?p6u7ek`|75(p{+xq zj)`~cC9Bb5Eh)z^6}r!un_N1Y%d*r?g=tkA&zM~pCd6c%@pkOPkI(NSJ{Mh|gGys| zwoBF4)Ek5A^V>Zu(3>2n7lb8a^oiLXftmLGXUKm!Cdz>XJ@h}CFzXJC$o4&CbDjp; ze4vX$Y839qzz&^NY}XWisrpl=Jt%s`2=xatjN^4}MU#`c2vBv9v|!P58`b>g#dA4B zc+Z+ejQ!~S9jP5&_JoGR&(v+vZ?wre4Mx9klg{pdjz@x^x4w`QY;6ItSRPOH&z+jb z%}JK6ueAB*irL+Oume=&H^+RX#Irn*afipZ|L*2LtHjwaVTHR-(hTN9}2u26JX>n@7<{k8DGjhsv;3z`b=m~XawZBUi3oYi$kiWv}TNU%XDnY^eAB@ zauiI9RevY-(=QMQ4yE~<{z=FvOcsu*x))9upNfoz5e_qE0TCGHs}&)H^~{y8*p%27 zW?OKtruxe*&!f4=oCKox45mReBx0-ExPAGIejuz-n6Lm1nq?DHhht_z%jP97iagi0 zP(B=hdv$+m!BGFlmITJw;*9UB*y{YL;<76?wsy%0%ghBB)kTzdWcHhncuSfnl?K)n8II9>zwr7L6T2H=-499B<8%};RFq55*X}HLflj1=XapN+^^ZMsVjS>gn|B! zmh4ek?-IW+`uTgjMmRhf#65ZjD3MPdV5l9#XT*wMKLU%?s_I7-cHK`uornn4JwHED zK{6qpLq7}-8LWIWapl?Tg&S4gi_EeIng|VCJuaIbXV+&_fW7x}j+)%a1;(NY?=nmK zJ5@c_CMRlFnwJO4;Jl21o|Upn-->o+yzaq!OB>0Q@*^S>mtVQQ9!#Pc3DL8S!|#sK zg&$3_=zCm1J2|;Mmo8CJwGOopTrDEGA_LtsbF;2DNS${a#4FX-uhNXshNz}_!hHq}hPqeRTL zs=cI@!kqdws|B$0Nl9!umth72K}lyanh{-5Kat5O{LxeztHLsJGu+@*? zrV}<#>dDsH==+EJ))+_z@*O$!(By)JSt316PRCsozf@_j?cum+>9Ao{kT`Vxy{ zcau}!+Gytg3KVV$GMrR<5Kq(JO@&N|P}h^cPkv*&#Q&gXbre^Oh!vk*1k z-6w(?tnM5vnHN~8baRoIb_)qNz0GSiexn|zx0~}bY~Adh^L6`OhTo_#zaj7?mvj7w z--YQvZ4Io<|Fzy3in;bb$Uxujpd$)uwjxgg1W!!wt_bC4{M7NRiYI;t(dfZ+FjIUI z1!{sG-#}thyQ5Gr>=^N#tpIa8x|#QpY>FR_kB$|A_Y5=-yF{@1X8(ryUg@Aoxz5q` zwB?)U4V8a-P>W+f>)(SZf)D?@*ipI0#Sy%mqrtYU5@`F^*1O=NviT3zq$$9vG)Gl19(e50u zJ4Pl&9E-A?()hIWjN-;IZv#HCaz!`yxlZDws9D;Q`HTYm!Enn(ppeJ}S4GY z?h!3o?){{M$+OBx>9<0Q@PR6?#yj9(Q@tH8jv@@hbj-w?>!%*uOWfNkRJ$D`6u1Alq;b-VEvr}x*32um0!07Jgvjq`!>kAu?1TDbnpgC z;?QG9ctorS4^#?BcI)4!$X!|P;Vw$X=XhH7>Im-Y|ja(>s3p+Azah$v8Rz9RAW8ytR$)g zV@>teR_z}!BI`rD9586^{QMNN8jNW*4DdGJ>xvRVj5uM1{RCy9xCRoCVd|Q4`(WWV zMD?@4I?4y4-S#`ENFqv`Hq|4CL_EG=0QTe<3u8e?Ah7G49u>$wakM4m9tI>tI}Sx( zG>`0IjQ^{hop6$~7(|YKqavOH14U@q6v#asP#yz%P{$+zkS6YA9#M&)vM?2@Z;kONcMt|4fOIU~hrPQI0isC)!u-c7-Y`f0+8b!6U_ z5}0KZ(8Xg}6Qvl=Y=+A{bL9c^vG*oPmdeMxo9kFzfw>~dH1l+;dGrXzaC+>vz49pN z$?5}GpxfyH^yba4gB+N(NlyYHeyFtfX9jz8BPm;QxT5^8fst^8bW#-*FQASlW4Zx0 zwuPW%49p??pCKDd*16y9pg?w8z=A1#UE17iW5TOh&hr|@bEsF#{q3`mBtacDnvQ+a z@g6ejy3rw%+s&s0tzL!{YNc+MDGSb_wWf}Cb_HS9jkgT?#~xmk@iK$TGtv+9yig9` zuC$Lp{AD5dYA^wfuFHo)I^b>g)~R<;fV z7!6tp&k}3&`dCrb@dp71d&)=cZ~DDdr(^wUd1OFrmYeY&1V_CxIg{@c21l5MbS_Y( z1bV}CMV54?S079mxSq=dCEu@4)0DA>iNf$$Gw4xjjMZV z4A(4q@}N981j#gXl{F^N1RQA7ER>UlUK9)}ZNxw2@lA3%@>Eo{?Z3Qmd%m~(8_ZbnC zEfnz%6fy(XO<4M$v-C} zYcCz&MWHWdL7DG&Hasx+)P~Y*6!xa5$hRjuluJyYK#Mr8O<*$bdVLulkS0`jCJK#j znFP_1(4n(JCB9!vG)Xqw&2_mA)iGb^Fq7gh%!v1|jJN zRrk-M^@^H2N+d|ajat5hmq~ys7Lf0=QLWQIYQyu*%Z=YVPag2<2tJYQB?4VrQyPPo zU~gbMcX#LeVj}bUBkK}N(J5*!yOk&7ti*s${kXts$~(ILvoe3lrr?KxG_zrR9fX?1 z7LE|>j}ZFqIYmz1Af;l?m>Rx86b;Mdo$G~tPuKpKvL*=aV5INEw@JFBeYz$R4eTE( z1wQ+IFcFAoS@isK%<|zsW~hlg{2}g0$%(gtkDy>{*HeM_&FDEfz0|8i1XoYN&BHp zIgf~W&q>Yu<;2q-w?iiOovo#WvsCh-(|>n?^xhtIxw}{7D9#CCio8R}b>Cuo*8@3; zEU{Kpy>6op4?bRheOz5^NxbvgWXOn;$&ntdd%`F)GKKgk7@z@lL2 zdCwQB6xG_~p=AC>t~bZ-2+sSLx9Hx}5{eI`WkPFKw9>+)ci6tV`34i%Gye}+T23a` z|GoRL|9_ye{~{(FqblQ^#fbRNO6-8j5=aOQdrc>IDbM|Ef{UVHwvG=-V8Wvgyr z>`cGD@j+x#S*D&{___N762UA&zeD4Gi8R9AC&6-x>Z4KtdU49`QiA+rC zWnC_}VJllr8n%dyC#D6Xk-yMfHEZn#V!PkCd~A^;@J37Kj_~@AniU^CX1}zr)Aa!y z-J#S)ITSfn`iYEYyyv#z`(enPE|)1!{WL=5tv52>d^(@U=c4A zoNO4}Ry{OqoQEW*Y8Z!W5C@YA*Pr(h#I>CFVG8#l8^~PrhU&R+sa+iR!SKMA9@0*JcQMZj0xz?Y>&*09H22^%a}` zZi^+{6e}ap6{~yyzy@+1oVN!5Zb#;N zX5-Y0!*<#2(jJ>g-G?1O;j>Btz_+XZqUl^} zlh?ET%|(rsm3B?m=#y!eBJtH19RT=yRPmU0g4xK!b@h44Pp3NayA@*<`L^j@muNyp zyIb#&`^grOc|e*OpN%nMm-gf>FHlWZOG z#LO>Hzjb^cllr?2Cj){E;!2*+60ECvF?vos^NOLfhwm;Fnt#ixcpl;FY(2dVl`0Y7;Tmq4%9c+0J{(`O|1hL{J z6Jc=Ksml~sD@5|fYhD>ee?%mJvmEnvmkJS6RkRiQnV6m8*+htm@mZ}0%R~K%+b_sJ z7d}tMWO2SNNX};`cYH{*O^y>x(PqGpH7Y-SV5i_F3Jnuu8o38m=;n6D-d9H4!e}&9 zn}SsZJ8A&CBH~v<{UQwY?<$N`=(^;1v%jic>jMnu1g^Qi4k>2-X+9Hp2v3rn|3sfu zm`G*xMy)_tihx{JgMZf$_eAK*HXj4*_H>!McUMG5#3`dCXH<(|9CG%|3W0nc%o6HCC zX=du8=Cue<749!H^m#clUy?yA7U7B*PT?*j;lj0QTwqdqEi918YnGr9K=o6@76U8X*tvDP z-oDNzS`0wQo>~tyGz-AplbC=i^f1W+=q|lM{28`;{Nb8Uj;G*MF(^eD*`74BT4nUV zxHu|&(s&bSHd_&ei*D*z%4ntMm}XFK?{Iy_D#L-M>Biic!j>n0)(z*e$n%$9!vErjOHImQH#sNaKSw1o3MT;P zX^4Rm)@Y#|x7z!z+Ch_<>sc{dJihbjMS|Sw+S1}N3KUki7BT%2a9^^$ns~F=XH2ZH zN0!?xh!*)`bcRd{W!o9_Ho#~rY!QL7q|X?fGr<)`kO>=fCjSsfd}=cR@b4WvgWXo& z6#VR^-gB9NOy*@s6Isz|z8__to|Y^>;%F| zBCW^i${j0drE`-nQ)$C~{u#&+z^hN^hh9SG2|yG2yq6GS59f@MAFuAhx-FxLGJiG&Pn zOr)J8GNT5N-_^55k^o?Ha);(XB@nSfZYXKsZZbt=)E!2gLTR$xJWSUh3*Xe90)N4! zNe~w)_CE(ydW4qh?K#CG)QFW?${3x6Iq??OHU&BQ*{x}zIhhsVLgo7}O`YP>$0!>U zMk6S@I46QD#GHR$mQDw#*9zK^AxN*6^BBgnA!ADt6nd$I5R9b zClW!RduB8RuaFmv_M6D34Bw#JgJA3iV-;0KY^Xfnuq93B7a=T(u(J=(L0%QKKs6=R zL}_tM1Bq37KBe3DI|=0R2li3=t9(ZUX?kERf5dcr+K#!MWsrqfN<DRMTxcL| zdG26=hA)-3XJ46^`bY|q>>qAxUp)*`b-!x2)I|6`8ru&MkRLMC4WPsf?B}Pp5PHpI z9MK!GQ7(i`lhHo-6k?BbGP40a@;Z}H@aE3sZu0j+5Pr3>5>n;Fl`XwL~NQA#ge8}K)Dj-CQZ z9L+QnMwz6hJlF+t?O1!m2eEowBa0S&5sprU%L-Ekrm74{7CNT z=k|PCKA~?=doG|)or^hkpwd(Q1-^OGFMFt#1fTcp2Qd$~sU{vuF)3fdnyY4L@2oO_ zsY$xVj&M%8w6{y8A)QwYM+NJl>_{biA`|LI%*JfiuJLfHKa^Ti+L2TF&|?2^bq~v< zZCiB@*-q6^t0(I0Yzs2gXr<2FCgu5Tv6l@K zw(QxlC}`Qiu+EaWvgJC5>M5hVslg|5TWdawECPhh1ZG@on8M|jL=6Qls>Ht7(*qom z00n#~2Bj$!GOKML)hRW`DoCSStc(?y39_6b-}dX-Sg@XEI#fO=;a+itVfAC=7U_0QrRZD0kAhNq_sjJycDBXHVAdn0gmq%9w?BRKnB0J^V@U~^h7Quu z^vHzvP^bsJrdyX#^COve0ZLFH4pMt{t@n4J?SY1{)pRh0A*l!;Krexq5TDQcYYdAp zND%Mz4zVAWJx^a$fEW>{yUQcpD$<+=*pD5>ixQzs{=>jcDDip$XDO9S!thlOb>M3) z?01&Bml7)NL=ipP@$<-C9Np2YzD$Q8N@jx901Z%V4}7y?lf3I15+qE_Q=iQhJ*@rb z;0xuvFQkD;!%ha`&x3B8$N<;1MINiS6hgDC8<2DI5TjA@WCJA7Qq2_}Nqlz4RKvyv zSiyp_W#A4QqNxRWFi=>}eG^dps(dZ092>s%yQaROExv$s-}X$JEtA=WL1zVxHWSJOg7g;FdM^4TTl>BUMmF- z%|Cfpkv<{^U+56KtL}4uheR=`w)PbAmeD!5WZ8%oOB^ACdKP)XVx3jf>abwfjs_ju z?4VuFvKyz~tHpi_PzIg}LK&#?j#`*eSs_Q+hSaw{~GP5O`TcNhwCY-FW*;7EM2mu>hqS&o*|EQ+@WQf3)Y;vS&_F!JlU}Y^NfXb(p{1zONyA4 z1d4_*!&%=vir;g-uPk7Z^y(H-dR*L08d|prau8Dizd2rQ?XO`hz4>D;vHi;qmbVD4)D~a zW=W-b?k{8twDuGIQoAWXsH8aTjlMAp3 z!(Ydzb3f(<1&P^RbH|c&fnyAfzy8n#MdchfEp>#`evdR-CGw@;D)sJb^%FzV`Ql z89ZSEIpLjK8884|lO&=V@6FLu9`h*aB#WiJ(=L`jo`Z4OL8PrrQcdGZ*k3x#) z5XI}_39#qdd$180YA&yrqsR>k?_};`e@hj&z_Pvr5n+yk@yC8nw?AUSJFEh#hm*#B zHG*46@J%?tL$aHIb8LXo=#IQ$mY_N?E!I5LqX1$Y*qM^w6Mp5xK{7V){4Z&0g`UzJ zUuM(T&PB)m-&+nc_}Ze+KA60n{Dup@GT$imT~y47k_&2#8QVY6C%zVeaI}sZ0o6#{ z%(T1%ueDY15P$K~HRM|;lbS=UrG7Xism>OF8yIx`A^#b-UrhtBp$-(Ip{%3m;ZL}G zU=-WM1_q=I{t~X1v`)Tk1o-0Qu2B}A4#i27TP6#T3k#|$rxTx-JXuOBSc;3dKW#^$ z{2ez4_He{7{e`#D*}i_Fv8UhGv4&n&w@y$$9EF_V$wG=%%M%oz;Q8l*@pBdtk(Dh( zV8~KwFL>IZ-I{@3a1&jQrv>|-JXWl+UMpTUYUEox{qUr?d8`}QZL~kyzavBx2AfNp z_7ew3OePOc`131T>|v4zn0v1{{co4u0dN6%Qc(> zJpx6@r4Gz;#48+r=x7z0t}E^1DD@L*D+lQzdqG{KCVZo2pVdg zcoRnN$l1-4*h@y*IUjxF(EH2lC1~mv21zjCW7qf$8ScFc^dYCQn1L>DgF~&>F(8cY zv|9Mp_4Y8cz5i+%Dg%U@wFJ+r)j$mW`iy5?ksf!IMCUYae8QXi`UZnXS*q1wyuS$U zODkzY>t>>+P2r4c+jB75NNEok!bifWZ^WC9>S2V&*qgOpAIJxi zY5vE7Aq_9GQoncY&(<7mUzzr8fyqBgR+-Mdv4$89z6U-c(56)niS^U8k|_9I$GokM z!te6TR!*TPP(i|LV~p?8l-s;Qqnxq`1`S0PmR}bGaD0=4khFj1jtn+?ZG4=o)1S13 zEPU6{(AYjK!;}yqDa-r5d3WX3cyhc<*d2Fm{?2YcZg|5jW3ku7q{Yy8*9V~6t!-Oc za{v;25+$C+;U&j=>}T!LPq;-(dEU{_bg7zy`XLu)Q+CT;YXsK)FhnN9IyrK#dw5`g zY#pxT0BB6{>92^bu-iGP@VEMDe;y92l}@|4;=sf|xLFEU#0|1sf8Y!9>Tc$~MD(K> z4?~&#aX4QP#f1$idUl1ROFl5T2KdOk5!t&}dAl*x4Z4kwu|bH8U&)41`Sx=RVdsBn zll1RB4})# zZg4_@Dev|6PqFLAaY50eukm@H%nIOyA6ng)4TzMnbE3vLmz&zXds)hJYgu`)BgeW- zGx(|bocAvE>B`45+utpIKgYmE3RECKXb3Kq zq60Qh?s?MAtxe8n{$|)Re)|tlI!~fTXd|>LSjCQ z2#stC-|)&2B|^;UB(4F(>~>+Jixi1U*(QQ?7uUSJSumY?e0-2(cl8Y#)&ibK5}L=D zv9;)(%NS^mx!GB-*O!8lLh{|96^^$0ZX}AqY1CzqWnEZBW~&;truM#Sf~7UXZHn6d zcgvxIBAESTa*LR|j~z%pbMCU7ZgO%zWVzQV5q?12>}J*fVZZ<9rDG(tH?o4^<^BH) zS^pn((f*zNf73-P4^FA`A6||1KbfHz|1UHMjeiYz!^m4R8i-m-z46C|Z`#~p&U+n_ z@gBP|2jJm0F;IY_$&u)k+L`hSvnW-}UHWVBikh05YcB$b=_nM=A5W;spBV*&8$W1g z3YbsU803?)Qm@Li6g4y|_itEX1m(Am*%WI2%zdpVNzlfz*+kNa=o#xn8J$ZU7 zu^|i)LF;YFom5(+cVq@f=&gC#uEq@C8J=QD5ktuFjfrvcO%u~xgzeyTplxZnHO%@$ z7;3XUmyF__OoYlm4Uk4JS%*08nJNe{$fnxLe4mdxG7vk{cciuXFlp@A#I7F4+u(l% zmo{JmGb%4LP9b~$<$G{Nyj4xqZkA=RGJRVP02-jB@pI%zk)wz{;3eb^rj4J)4a-0K z&3->>&|P;%K5m#oBCh9}V@)HuCir7c$G|diX2S8RXUdlurTQ z{-W4s@tt@~=>p!9_1k*!@v%fK5&qcWuv!3-!dWJ(dX;@x|MH+BVxsX`jzQ)R;t>Jr z=0qpu!T56lA|9PKb(RZS1kd(57PcZ4U6i|eT)bS@xoeZaxIpv;g)J&CLBV7fjBhEB z`_xQfj!9$EY5>jDlM?#=?@JBe6D|Lt?kR{voUJVI7kTL@GSXEPo^cio`sHAHYXs9> zu{IqJ3Dm=-!bl&SGR`&*q488=i)+-^lf>{}a#JWL>uQjvTyVZCpK90-b$65JhJ=1Q zrxF)-wn5Z`VX4o@4~x3fK8}tTfq7JFV>!6BIXqT)x{6J9+(-#vkcP;(i&tm6zsIEu z14%ti_M;nHG@ghV6LoKT1JA{5K4UcF`u@qe#D zKXo{sLtYUTxQe`wH~v|Lh<9~qKGpm!JXx4CxP$+<>zf!ks*gr&ds{?{=TAb#>{nZ^ ze6n7s$AMQuKuo_e11b$eK)1}^K0jWsCm*TgM*f+3;mE&oA{+cHBvTbE@i^%N?%}Ha zwp(U;4!SPq5kT7j7oUZdFcP_nmTzvB(1SEl+YGkvPSs#I_0Z@yKMAnm-l8NK;z~Dw z1CS=vKo?^$ltdv*>1dHW`@9aF{@5Qf{98G4DHC>^N>jRp^EYl9&PX7M7B0;T!u*3_ zRfz2tMkUY7TnUw-IJl-iT(JH34{Xvt;fu3N?+>JWWaOeoe#Fd2VXstcPj+QeZ@Bh^ zXbvT_V`eLCj!E7K4r1P}T5TzjZ6C2) zudl5-j&nnYDxVblFy4xXj6&2xo2w$=mxYrT;Xjzk!IWZ9aK)c}D0*7~qzivPDT~#B zDaY{COiMC}9wtWg1$&NG;W0}cz$gu-1wo$Z?%Nj;+uYEei>vu1wL7ebLzZ1jM{a2V(L`3*%&DM zYauG5YpV{(voe%ZdjKfrTSNJMTW}D+2V;yu-A#&&#<*~t>6{vr!gmbdFSq&w^^EK6 z1>zkgT1~EjjkDpw&(=u|d!xb{^etq-dOL7fcSj{W<8Sjwd5W=v9__jVgdReO5s9mc z^WfL&x=30TE)2>E5Osq4JnT*n05ojfwTp6z{yQ}0iKB$n#Clz&lrW4|g<=+*H)ms? zoHH0mLC~H?(nz+E19CrwKseVj{aR`G)pW-ozBB`rQOl-)b^K7RSmO8fQ;z+K(1b5> z!uB)B@_n;naSt4eNQ*0eZvk6HE6$ZdS6|UntaD*G*RjTcGo3tXqEH zn8U5~79nB$z{p46UX^b=r&OP|zqZbW?fE&{x1Yf;Hu+k&b^>`&c6Is$#)YwQk%(KG zS~$BKqw5aJb7!8~FEEcD<{_Kc^lJ3qan5R2jWRh7<`2Kux|XL#jU*`|ozhcpMdRQi z0Z}oq4#zuF@FDE~X;}CV^&c$kod0z(eYLhs(gr*7|Md1rs%f!8cPAws=-GSH=;&PB zafg#a#)`&hmPm#ciRXO2f4u|qlg4JRXGTG>3j%GuzC(LfhNrZ=k5uOJcX>T*$EkOp znEgJk<-gL1&>}ND^)hUFZ_@cX57~ZyQTXwB;0Kfz$|km#=Ncj7KYC_E$g5<>4SAt} z1wQOSdis4jyx2Wttmi-M)L3u`V8mG`af=lnTyIOCm>*zkpQM!XbZjpV*R(?Oazrp| zy$s;zn&&w8i1#c!Li*Buz*n<6-~UCN|4V)U{N#n=m9N6F-nNnAySC^_W}NA!(Pa8X z*tJn|{m#x{z0NojPBh?ibVq9KM)*1{-)(7ic7+g1KgTiRL^EjYXtPeCciu$g)^|RP zut}-v7s*}<*LERiM0`wN#CCozrOjIVXIPfA`=lc$ZEa!nCtk=I`aQv#*my)xl-YD` ztdYhGS)nM4B5{ki`RdqHV9?8ogD1D-pWeaX#vO?K;AOc&&Q?PJo3&j-vh#MJ`}dJH zl@kh?Lx!BdCaT7J%4jo0=n}$_GtKM_o&MqQAt8MAp}V{y4F0kIM7HRJ>%t_!v|b=t zy1uUDnaEx(aibG`zDu@1f2Y>Blq4Fp+CV>a;Ue|@bHWSPuj1a8Nj`}awwCYMV%-Ru z8Kjw(4A@RG08<-Ax)J)2d5EOc-Jxu-8wi$|PN6vtv1hJ56 zLLOZS@HS_%5bP(*k+m%t{cf7+5`an2$rOlF>YM;|Nlc+{Lh3T$0+bL?O?{f+K2a96 zNjp)a)B^qQnP$3nal{|=#yQzfW+R&R#GYAHW~AFS8RH;4AE%uuK@=*B0xXLHB-Vi! z@2FI{%Rqe+5apztMkx|0$a>I5%@i>ihQd^pkr;Z$`qa`>5MSoZ*L2rDvd~{(nuKG1 zgUk=^QKvAo@_UZmw!OkDgTcTP7hBBodMxsfw6a|mFe;|<(cT8OM9pAjixh&U2@p#A zVcTe@5OYIobY8~dHw8!Vx;q5V^Jqq3QSA(4G<#x?^X{v>`?%vZ5;x2`_?vF*Xo$72 z@l2g%e7!W1ou&(-g;1uCyoVG=MLE9Hr$+ryWRvFkq><2juZ~T$q|PKo@)6a%55D@V zp(1B{dXs8bH-fBqp{|{yF7$jQI zWQ(?K+qTWqwolu(ZQHhO+cr+yw(Y(>GdJS>`6A}N{cj^Osw!(&MOLm{i^3IcckN_{ zzx(|lIku$rURC`V%k7oIA5HAc!qe;N;%3_-lR0Zi6Ch#0b|7wGbFLz0bpE9i2+}IKfS^DTCp+ zlL&tR5SF9$ut{fuHF>M36`$O(hoP0V^=cNUnYW#Be;vKaFb8V*=bux>_ngNFEC|iY zeF$KtERwd4q_2F=7p4FPlj1+w+B;E*n0oP;=y*7=L%@MT`Y6d?$e2GQ_CB0p{RFTC z_P|%IS|;V#X_uY@k_R`?)Q|hNP|z;(M#qvGjCvJOcd|>zqw$Vrbi;=8=NrXw{ah|+ zg$|?NRj>6AQ!*T|>1)?li7-XXi18sknI&slMHMRBW%&YRj|iojx97{veqNkO8-Yp{xbM)9&ZWo9q}kMA>Q{#JFUMPbk_Ak8v>p z3pwVIUQKQnZb*c!hv(Q0k5jUO2!;!4@*kdcf6#U&;=h^<>|bJzOSqO8*!kFGR}|fu82Z^w zN*P`~KsoT(#s5eiZ85+Y#k;}|L+G+zEY@An8~^HvhqoW6U;6!Gt>yPY;hgL$g)XBY zAYy}EBG_|{Yf(8DThb2GpwriFbq>Ei5qo>U9B~rUr5C&+%vi2At=8a`>gBZ=MgOh1 z-i*{vhFg}*@xi<%l}EZ1>YLC}Yf;FUUY}a;u!%(zgo?XNL7ATqb0MJc!i=0}tu_V! zk>^RlWVPh>PnNXE!y!qZGBFpR?z!E!qN}L!Dq|m36+YyiSwU>%sX+uiTxyS7IpQVi zvKwT{P(g5&+L+37K66Udf?O$H-}i6u|EpBDsrcq*j-FVgl$((nqayMxA2d;Fs3ylz zSh#A-_XN>%qAqlp(#|)Knx40qzty3|j@9@()z>TptjJ1n!Dnew2k?gPbU81Ls6^EY zl_o_#M-^jY95~fc8-Pbu+7nf&XU*cs!?bn(W~8C&+Cy}RZEWG+>zv>iVS`d>{?^PJ zT;_tZ5lP?-={Hn5sP51JQTUm{^u*#vG0*!kON;@Payx=*SWsutfei5CIV(NHwAnf- zY?}7&oESUXME2@LG1>)^d8He`yxJ#9N59J@$cAL$6T@@ma7e`$83D_zIi-G*5RWl+ zdwQ0ZP_k5rTg7ePj{A|R%hEo2W-jI(Y{FuymjzSH-^>~plSt0oex*+PNIyWItsi;Q z9So!87=u{i*gd5{Cre=xs;AU=Ep~j?T(+FlP*YAxP`_CSMw&UhaEplj_oCLtK zCUwuD*@rDFxDSveEHc}0-RM>L0_Zi)-HB+h02KU_o9%V?=`#sUjfZ(GONDxdJGhdw zO;uaiyu}M=**U<_bcf(&wnBm9@U@b3fD`UX1?rD`&v~-|!S(tH>mMzl+e_v8T12Zm zwC0|UhQ=LqCCHh-T41>UViJYWAlTbf6TRn+Jdq-F&Bv=x9@u@39HI56O$W?+ea)Zy zgJt?KlEKN`2do`?$l-&s#SEoP?SbWPJ_ExJ^|zuXe%V1~JWTXJ0Uf=wPMHo5YHN9j z9Ugm|CVM@8I(-`#lne|^R!hvHZQtrt(Im!WK!TWKN7fAXqLiq1yWEp-ItvHD)3C8< z{@g%9l1qGPL$P|wpr_9;S0$>L_1-AkW%8m1g+>aoQ1@_ayH_y2v(#ofo=!V;fJBvvJ-G01pB;OMn$Q zw?~&e+|dTIQmmSt7A+0q@o+Rx(?L+swg4#Jj0S73Ifl&ZWyHfzL8#w~DZ#7k*X7!d zJ*D^a0_X?+cwFbb)%F0rR$!V_on}QlDVt@FG{|%eL`*y@UwK*6dXyNX(n5-QGHl(A z&8C$Kk!hyIJa`JMUiq;lLo?Dzp{EqC=GxI(9%vl7hobnnS9&F2yf~g!)?3LgkR9l5 zG&SG^1)7U}I#<;x;zA^JD8i@w2!7*I#L*2Y+a+`~`tlB3(*&5N2-pLO6M-_IPs%|2 zEhvkOI)6SQ05I7La-VOVGyrt15Q$0E>RMq7~T+`APkP z@G3;uOGvr-@+N#?G4%7(P;gL@ZlhWQwj3flRLk=to z-=U5*biMdQApCJ(2jOZ>*S{DB0Rf2mGu&y}4!J^AFN{Ez^YJH0;5uum=5HiGWOayJ z(yaA?!RjMhCWpN}_*4tccBIN+wezA>A;y>_y=V{1w8Yacv!YQ;1mw{vT;Aq)Bq6|c z!a2Qx6_%6{$C;cTIRv<5oCc3s6&UdGGz>$7wW+;Y__G5cN z%ONqdDSOi9U%UAS!DQ34XbO(E+sfAkd>?$0Q7zX>=2@gj0D(XN0EFMO=|?*M$H`V} ztZVI#*rfU{{qi?g8t=723V&9~NCDwjROjp%rfkHF5QsJ6%=QAbo)Eu*4 zQ)rdSXGwnb*`e=dX!y&&&#O%E$Lcv-0dvnKZZaw*|2 zqWwI@7FeB>FgI~7?C=4@TncaZS>DU*4!->G;VX2L8x_ZFuc0ygz(%bhz7A_LYd|^H zt->W#{yJ9x)`VFNgb6HZs2F@1hAO&AgWkLIRehEaoVQ$pR!$zEpC?QAs8_?f9&+HB z_(@?~`0M3ghXak4YELt;%Y?%M@0oKzpudD+Lk2{y0)@9z7*Psd>;E9FRA zM=smB$P(Xoi1M%s=_6f+*Jip+#y75-w`gd=Lwa-W;8vsHb(lT9Vg)gEuy4HuX0(sH?5 z*m%rZT}^l4yfh$X8_@AtX%2)#__R6~CC#nku}bb5aI~C2#IbQX60X~y+ne11#Funa zPse?*-bp|nKd0@p1-Nuih?E{{auAZ8KVR%JI8$7)^8dQ~XZg&}vz%6o`{s)V?K*78 zG|@9@v7mkrY&GG3QgUx%q?3ALvd(Ij6*P|P$pBY>s+?*cMNRwcy)8Ov57s(r1j+*IqNj`&3F&GpT zv+67psiT8Ssb=9sqjXw7;C2ZrB~UftYHIzlV3DldFp>fF52*NQ-4I{ggX8`usNjw@iTCDP!QdGb>8gs z6lQk;g*c((>-cjF&;s#)p6uOixdLo+U7Asr@YcV^GlpCU7hbPo5(hGQt(LLKh-VEm zn-G7WSb{r3lWb0ahS>kI2)As;8&k_G{Ea%l`UcNSHVZSyN}F!x?=5hd{~AZ^0(`%4Ojt8u9g zTb?IYuvP09NF3$HUgENf^_7C!qw1rme#zi;L6*$OJ%0QH;d{WKQB-+IAMR@i6<Ef8tfO=$_i0p%c!y`{1mqY z^~+ahC_={~a*Y6l*GO;kTz+lX);)6Sz+^72-dX-(=TS6TD6d*@vxx7Ml(O=-EmX1P zZ%|!PBV7kEMSYXTron^C=^dzY0T;>6GU1#9(yR7{T9^{scvoA-^CPi-&UBgeOhc1Go~b7xJ-@ zhxNfjQ7j?)vChN1buA$6kTMx_c}v&f@{Z-Sg?6!FoYAS`jaRui(L* zD4VAUVB!~p?1U$wxQVCIKy77OV!&2GDP6aRj9S+Z*on`F&CNrsjy^?nf!k_Y575S1T1nkwc0aIfx9~&%9wrMq9^5RlYvJ0a{VC9=>bKv`Mq~$7 zd|CtAE0)KkS`vDP@ryyynsuS=QM3`D=%3(r5wRq%tx>Xf?oK2WZlZEcAJ&Kmi5DH$I%x=CIb*ntTsLSapdt7v za28;CN*pmNT`$;{!T^uPVF{2o``s1T4!hyCxZtdaz!W)ySJ7Q6t9>S!9NDaxpKXu z|5{!`zwxfxF2gXh$5B#naQ-6dy?`J~9H#mg%i#dYlgQy8BwXz#I#(#MHRBCR04$3q zj29}=fu)QbmrH2vAsddCWQjpWI1ThtTv!d&2}$`l3^t2BXB!p+t7y7u*=0H7gl71{ z@wr4XCO@v*z=3Y_jZY^`7!e)Nj}C+8wFPAm#$gE{9(_+XQU{(Re( zZoBe`s!+*Iv+Vm!N_qarO2_z1*lbN%c*85TO5~!_0{shJRx@AXUgOceo3Mn%8H$Bo zdA7$+JBcc_01oljY$|8?8)BCd(d#p|46w)g!u*ZIVLvSiT4DOk42c**;9*#J!Sst; zBsu@IDOnB}ay1gK1?_lp08~#mF+Qk#Nr)CSt~*Bds*>$7AU~n0F>vaeW1)27|Bi$0a;E5wP+QS=?SYWhRVYy32SJ+W*JvbY<$%j-N7 zqTRql0?k=k6h+=62N~HoXyxp>>Hh|uTX9nA)-7(>Lw6zJO9T(C-z$#qt8F%y)MCU_x6p z56?@=nyA~4JWS|0Li5zBESR9@Tf+zx=_*o6yS*YdsGiO=*|FDere#3&yp1Plir%H! z#D*ZWWC&=he1c$hv;DH9C%tyfqR{uT{#wDM0?Iy<K=(pG>-`bVWf!@GyV^X9Mjx&JI3xe&^Y-}O^%mu@|6x6w_gvgzQ`kHI zdz#~WJUtsP=YR(^f7z^&lYBbtpIb^g>Fwq5^QnIzV!Zda!gLpuj{p$8om&nd$Yz?F z8RBYA&LoR^aMC4@7}esrvlYA9#H%{WAgugELGF7*O5iS6B<;JV!;t#%fy{{_YD=nspmv21PU-?@*l0^oYP0yfGUapjM7>RgqF0K=foBW= z^`Q&K`{c`pq-IW+$#o&s`WtB ze(zQ=_h!qDhx$#QW2-|6OJy>=gg?8!SsK=U2Z|E@qq@UB>7ph%FZ)F@nZ8qu*MBwp zg`gpl;OC(zj;LubSYzCdEgGpyD7nAoI_;!-k-Jo%sewOdAl4Mh9~HATd^k+|D<>Tq<}W7)T$&k4b1nxoeGDs9ul< zZ5Vstc6|cKbwSs3OI1y~pux#=&-{X15nhZxnID5 z`z=CU>SsjokICLk9>Rz9V7_PKfhbcEJ zO`XxE+rd+&mhxKZ$MYF7A0+hz^wZza=sLxY3}g*r2h6~=o0tQ?U%b9qc^T8(?jlOL zV2Vx6v@ak_LCw@nA5U;9()dzr%YrrjrGUE>#Oa@dgCxNL24@I4)28n~_IO>8dR8wh zRd3{1d8O1Dz?4HAeHj4L*lIk2&OahB?-AZ0#PbiNdV(aFkjDBd%}jp&Jl}$=2E7#b z(6B{UP^A>NwJbk{;r}++6yx+*9;GO*BMG*WWXcfjb0$P48Zw2a@7V(p4b9- z=>q{jB^2~!`k39mfCD2fVuo;MisO8c>FG_awi9|%yMkeL>_^I5c%=$T{P9J`D+M7Z zbeJ)@PygX~Sr7~@rt7`)IAB&pNe>4~PjBMHeadD$_Ir-BL?8p}(-glfED_7oMX7OO zefQ_+uoJZtm+1DF8fCju-~Z$K=*v#+U$MppO1egleYuqDSsFc3r;RSiN;HkqPfl?n8lRBpX(W&Vw9Y&X^1snTw0)8;#!(DfTDu#k>jDv)R#LRA@PZ&? z1rLNu;h}6Limr?3s_+p zA)4j&vLDkVfD@t=&&Wq}P5F);_)Z;UICJ>M$KgfO%r)t6Oj?9H zWLXTNYt+F=S_4;=vnHlBQ;t#6d_=H!pWnBT0Tf6C+FfC$j}-=0h4&?2g0f|0w9QHN zd%8;*H6plQDJywRMPUgEuVtd?4YF_eI>=Og<%e`&!Tb-qH`{;EvSVgu`@dYM|NHL! zi%@dh5Jl>_P`!otJHUAC4_L=ujbzJ-x}mOKuatZU5RVWJ1u+L;rvCP{K@R`|X}h09 zHy}M*=;`r(_awh(&CTKj%Rja|VZ>8?<9=}_Dp9&k~-B&d25$!Zr}xa-_g zn@Mid%iE_4?w;r@qTP(lN6cS{UN3nN7xXVa0?EiE84}sVPOe*=%(=G#t)A2hh`_))^ro zYiCE>yX(&{P$mu@X+~^zh+{s_ z-)Mm2!)<&qsLt_TT3RNJDt8_7UrCm}MOVX~BV%;ay#$x>dIn}q`Cl%l*})}?+D%YZ z@9`y3L>dpeUo@B7Rxsxp=7@QLGzvewp()YP0_A~;R6;A zv6)~N8zN-2i10mu*tY=2$~w!ePqd)H|oc=;=n)iPQZVpPxyu zs)|T7de-XQ&GpY+o?pqe4#6M7rC&jVz0}eDxxbk0eIbv`9ps^txqGT)2TmM&kNmew z`UxzWht)>WMwuuOhv?$U9i;7N`%6e5;JIUneefPexEeC*3tfod5*&LhA+qE$Z9jb) zTJl(lcWyH4n8CAA^^D{e5n8%rsN^*885Is^gsw)5w1oF>K5-xZVVrGk1j0|ZYNS09BsU3BcEosc zEfgrWA1G?ZOnuxV8qMgS_!wQnLZiLSp!3E!tf<{f#r3>M0yd18tqNaIU{ zb_??5!upcorP-vtJv9#aT}%)L??40iZF4LPxCkO|9T!C0$u z{(Ef$wjILB7`4#}Yda%S;y?+j^2-iq}`AjNRBm#l!>E$sxiboM63Bvt-={AF~(sog?q19C+A($0E z5@y!*)qmek=*W>Jhn(x|Eez*KZxT3l9k!H3%6}wN`=tQfuRU4@RWgUbJNd3sr8XnY zjFubeur)yIziXul{?=m$9nNh>R9wH642S2Vu#aQ5lr>zh+6gqF;nadWDKwS5XN6le z3z$XnJCZHIGa0WSgB4a7v0};Rzduyire~mU;09MM@os65YnqnDA#Z0@>K*ix`M-uq z4RzVUHbUv&*JHk9jsqXm6PVvF&tcG@uMaMp?WUP2;&yrLGV+m-RrgymN{qKw7)L6p z>>C}2pMU|+q_xqnhtnkDKFnTx3P{`>LLk}TM=I)AymJ@Nu+@&mvRCLD>P0V;+ktGE zMnGtgwTuFrZgre-z;VxPv4}&B!%_H{z4Ij6K8TXpurVnwYf6_(gUQq2aTE4-lU8Ia z`U;-lhL9X{-|u=c7XoMaJCD{Ua4upS6H|;1c|W)7>8>*Ct98*+;e_jhTys7v+yVv| zdSAM^1%<9^u0V(|>CYP&Jfn;t`tO3kiO!fuG&7g5Yo$IAxJmu-oC$fQeGX;XIqBk0 zgu~Q8ahA5RUG1m z#W4k6N4es%#8`;uB;xns7KV|m7zE@X9A#nm4h)DAA*Hf@=FEaM*JvM$26Vm#wNo?^ z%44h7R>P*Y-G4yh494I~8nZ*${wNX-smuK1ZNLNSxHc&)IT|DK>UWI>OjL*??@N`p zTxuk+7}}+I^H)!gx(m0cX-i2$3P(sb%-ZrAFT(&{@;zvYY-Z{Z+Vzpda;44rFO(G2 zK%HG^rj;v-wjF*3$i7~lza1!MaE^n1nKuwjTdMbsz0NiyMNwx^NAXLUO{&EC&)xHI zC$`OV_}B4!w!s9euLjd!8{jAt?I?#T*Hd2#6R~ymzP3-nvXBi-cDCupC+DC?6Vu}lat=y_1dDOP^$ioMk&hO^-1zB&KnR5y+G^1NW%eFB0?mvl|`kM zAi_Iv7+gbdcqgEVTjT!u2t&IFQVgCvXs~MrkD>gHh@<8d$sUU_(n zL?%;_Cz2SRwwl7ShOLyWP=6qf>Z1kVHPj_924rsEr+wN^p-I*Lgo<`);P#|~uL-mF z?BM7WDnuhteRg{IO|IERfdtp2NpfVIE;lB^i@`)LN)Y>$Hb7q3y*4B3!1RKVt23y( zH`E5#bkpi8Aw9*WO3WmX{eR1DSe8H2DjR#hE3XG<>m}6J2wY3R!MG zQic$hfUt0DF~C^~#OS7qW7~9RqnSCJ?cqFcKzZ}t3iRe3_g7Tp_0B599<8H4hKko_ zfx+?F0jcjj)7am8N;ySihPRnt^(b1fyzU zHvuy3-87YIU`4{x*#N9(Q^@Xa$zpwifh&oM*w2SH?ZxODw1~d!irJRnWZ+2NA~IeZ zEj?MT3c8RI=k*tKe^O%0-M`%BKNEJ0JN5}(A)%JsnWr>*3ydrqZX4oX3LgKe{>J7- z#hiHtFlf~RS#8SeG%BJYia2C98~O~g4Nu7EKp9S0CUo_2dGB+^Csn?sspGeH_N%ww z-G8vN?=Ty*4{yA;*jiG&@ZDyeAy*GtBw!$4TNn>&9OvXw?ETApDN{`ZICTXs8C%AW z6-k`cgTGMb0n3eioZ;cn4V?2r^GOe6sqN0C5`ZIBc?bW_$|X&1EGji^=Ls0`<*zfj zH3(lx6=MjO?Be#mf(0#KdE}$>$~ipKYoz30wfj-4Ck^+aarc=c4(=lb~wen;wbwh^372%XM57Q?gNW1Z10dWCwO2y0)3*1|^_^)u-#hX9PSrVYc^f4* z+Q`BOIMoi%!7^g{JNUTT@AyH5$F)(YJh$3jiZ4JW1sLh9EYD;$auixX1!{C_$=vHb zb|BPATz9JmD@Vkf{4r_ZS@eSI7YE>Zn!f;IdoFACsR#vI=d&`0T@cIBLyYRa=bFpv zn>Y4mf+iq;OMns^jC{+$|b6 z8fQDotZ&Ou$p;J{kw(L%M4UjtgNkb5uoAdVPqy#_m>p1M{GWm@_Wx8K`~O};8vR$w z@mD~+=cab93EiB`fZ{xRgYBQwf&^Lh71?iDh=!CW>-->Ed2_a|uAkp>=UvN;2#_Ga z=Pik`9q!m}?2;{dWvXogk9bj;aW)9Ls$h8l(`3-p{jLGB7hEIFyT{$hB)hpiMe&5B zvIMAT*W75C;@sDb5Xg{)L#Jgzq&isVoU6jRBGTUCu7T#c3UnlBqO>zhYd z1@5hBQ35)K^4ll^S;b#0_+zt8sH)G09ebsFC;og=8}fU~|taM*6leI$9zZD4d=+%oO|8uf28MTq)nT6!{8NCFH4*c7U5Q!;`Ynd zeXIi7kVLe>>@$RLlY=g0JbC4`c8cZY_taBDPda+f(l!JN^4i$Dy0FNJ-o=ymE={IY zY_BtITBO{3s`QGP%&I-iX=x=+G4v9k+tJNO05c~96KID%+>RyR>%o=KZ3NlWvKpg%w()rf@>wi;H{3a?$-QURUUf4E0}=I4!1jo197Pe zLO>2kI~cO2y-3+JP4+FmmV$~$qYT{hZ;!Ea@iR(c3Zj87UG^cKz=!z(i^V~#eZi9* zIUdOFUO?XTd_p-K#bXH<b23Gtt>?rGr*>f;hc&O+IM2?j;9BRK;I{7G6(YXdWqNtgn?)C2 z?Y`jDT~R?~cdxU4D{i+WI464(c;rnO)*M5;VV{A;3^!Gqn<4lho8pln@u)z*HG?-0 zK2mS#AXjWD#+pTI7})ZU!Jbc4q;LrOWcYEUwBigxuBm$0)rPE`6UU{#4!l2yp*D;) zvgdwLY@Ki(fda5s)08OQ_Be+}H z3asm?{tokJ*DP(0M^_TXAl(EO&7VKgjyF>RU!jwt%=z5hNX}=b44R>gE&7(kn4RgO zIJ4Zk{cZZwS5{`1gB!)3dV>I^&2a2qLGdfm9)lS6B)r{m_25y%Ec=td21=&nQ^YTQ zUPt7@*&YvFkvfF_JxM(Z=cAarDlm~2lRy4ZUEGDNCvY4n@L8=+B6vq+)l`7q_bR9t3J2~7$JBp)1a6K1D#=~YoZ zIVT6_90`fNC=n`9Qugz#D?Y%luxSZ8qS__h^9DJJhbH}_PdC)nOKNB(zHQ+qvVL62 z9}jqJdSo4{djeSGoBU_IFW1g{Gv!{5(e%fMrPRth{V?!e=~i`V6=LcCm0lD`s^e7^a;J>QQxMi?jad%V(I{Qgy_F8G-D~^vah|~h_TohJPlpH}~8X<;i zqsByuU)#>%M*EW>x;8g5$#qY|&3BUi+L&}Um9U3YwOmIZ4E1)lx9cmCB{oA0X1N-} zcYzxNP~Yie^trQb_}yZV>IN|C7LtS^!>|tX`fFPu^S~3?xsni!a%l6FSr?8bg`%*X zM?Z!O%8+Qk=k;@1jwiZL#anbkgV*a#H1IRulh-{hLwQf92DUd&ifDn9PxV1`cr~W& zDxi?;s!u-LB;Y~fjF*wdpe-2*CP9xn!5*Wg*G@o1Z)F>LSVW1uZ7xQrIcDk$Q1^mw zl7b-Y6G+`ZWYut)oN-T8dns$^ZBFN`IzS zuQfdz%=}ez(#owM_mbvz60TedJ~}T}6fS)P;nG@}tuHgDHD zu|*d>eecqi397SFOfc@}52T3(RhO)5=TXOpRi&%kJpUCI{?V0!k8K_;@9qdaOpka~ z6hJ4d(@b*&$UT+N%2E1!)j>=xG!&6@UgVR0sa=B{HqV>yb6X#;kp=1iB2-E?-BM_b ze~TKgkg!t@Zb=euFa5&RfXTwC{qVdr&{#BmKwEI}vg8biqRZwb=rj03f}qhurO7Bh zrVL2jd=?sm1vDobt|n6Ov05$?9*I~c-0i3N<{9Q8>-36FfwlxpPLf>|6k7O;TO&NF z%eT!iiJ%DL!_TlqylV>ThIM&o2(-hX_-cQ#B#4_WcT-8YTcildIWQG0mvw9Z*kh zbKpS!t+gJKnp`mBExU+6`=eAna(NbVu$?l4qs?Xc294P}LJrn6fueg@WVvFuq->(c zlBPq{`oj`;x5+J@ZwgE|M;`DkD!6zWOtdhhO{bHJol}uQB)Pnlv@gzz@@*lOh*J2B zYQdxiwaNXev~cNe7SgckOZhY3&uz*Wk=<7TRRTer_Ly%WasZC>mjCB25kf!0h3 zGpng8`1uNe`E2T${#tW1mBuv|g5J0+Uz$vt1lKVY^Ew$x`E)LVFUp`2)JnHcki^JM z6mQDJF#ip_z8qTnpLVhTz*fY_^egD_-`D@Xi&bYx*>19-1iw5`H)W}xhP?Evx>zHk zLk0j<7O=Rd5l$sk#?!vx_z=U+?xmTu;Y{6L9G@gkM-n#y zV}+6J+c_}0VRb=Z)DMoo-TF}5yzCwRzH`yl72y-;%OiRaDedgr=)9O^|JWLv+E}6g zcY$9U>x(H@YPiG5&iisS0*~?Z&WYXtP;YWy-1)1l|nYCrL?aMIOp5w)g zv5OJndn{!O)`V8j?BT_WW9#!H8>BoW3V+#7VsOkpJ0fD}v*Wx7Sm4Oio39YddF}_( z7Q9cyYzH@SG5B-q1;uZ!r+$EW+wY&}B2M^Oe_LAQU2SlrM9gK2X zDZ~6&(T{_57A%mv%J*nMh-1R5nODm;Jy!|#u_`_b0cVKsfi|tuJJY zT@E3T(p>iR>0G1>szTbYj@gui4e;HHBM52j3IYC4I8GoKG>9JUC7-7Ekjwqvh?FZP;KSj;Z>9z#Z zWyN82!L_F>?AYyuFk7h!46abU$>>*wJ;9p%RTxv$zOAO~*w=8Wn|nIfW`3>MlQG;t zl;B(f)$Qu3u?B}WuH9x0r2hxhUVjZyT67*T+i`twxz+z?qtbPHw^Gz{4LKvR0-8r% zPwN8?Z9?aFpt2Ew*wOI%la#ej75uI=-0I|C{>!ol-|O;-p`xO5KRZmQMDOoI-rNDl zD1`d@J3DdCCEy%l|FQf#6e!)_M)RLSSQ#GZ=Q>nC`^-73s$s*U{`d18&#?NtPFfer z(vDqE;ErX@JLus5YF{l@2sSkSMv~Hh^`>WY-K|Xi^}lIEOJhk#7y< zw>#N7qoF^?|7GUCTq#`9vnMHZVjD8^P#Pz!D!c8oCLZ_9@6~xFfSsa-Gvcl%)p6If z*O!hUi47MRfjc~O7GCxhHgM+W|f_RStr z_1ooT>?Kn}G%H2d!%<9@;p|vP+owm@rRp@};>ZT2UiV6U?`rU1bmJlR<>aiiuu0YU z=jP!1dGGw%rTzH)`QC%yr(-8q+xzV*7wl?lRE(G1XFVFF#Z|TS-%RRg;OzlkPV<3? z?TGJon?C-3SPnV<1Gxqh=Wjy$|8+V1Ol`4}j15l00Tcdt7`87PbBB}I z78`s3AQB^~5C{VATCjgVoQ(p2P{=oEc@A{V{i}WJCiQ%JB!d+UjKS`yoc7FV}{p@o0{jhlg+#q`|f0{n`{IM@z z7*o~Xy3)+~%rj{Ie7f#rUs;G@XIdU!Md^yQZ#$I)nW5v|(V}j*vp)twRL9I7`LjAK z*}lp#-qfU3lZ40>t+-3r9IshZ)D#{?!x9u1?{BZ4C_XqaG$y`>{Qhc?(I$?-ytAqO zo0_Gx0Sa@dcB|Fv*l=0+7fVdZu; zTc0OJW0r3KttqEo7W+}KX)Dk#D#E{*H%CLoGhBNK5kT<3T?NO zm*xFCL7CTYu|G4H<{~szZ%mo%ATvE#Ht(|+=`z{T`rnb4=>hSH%!0ldYyY4SOUF~! z79&{Lh&UThed#aHyoV1tR!5KQZ z0JPDUj_?94M+u#^9c#;l>ZV^n*#RPq7LT^OqyMgpD1XLjE{%BZ8Sec7habkA^x9Bm{fdWsVvDm~Hr);qM=eB1F^tJRw z!fb3?^Ntt-+EH97d2?*4wbN)6E*fU<5?II3x9U4uy-d7DxUQIVbtdde9}>bWUAUx3 z6xXfQzC!_LMNP0>PJBGBA-0e&5BQsM8Zu!>ZEtPsVGwlsPh#|Wg$YrQ@mKvH%HAPb zlwjN1+_r7owr%@t+qP}nwr$(CZSS+KI=32C{}}H!-rvi}5sheN&J}B}Z$jR_29<~ny z?Tw%90mm8G{;8^g87tORlTb5OUk)?36v1--?l4yYBv-=KaheT1P{3(IiOxGdvY76> zGLB<`RrAEleyizwn~&NtLM72ev5pc$Rdhc~Op*Gh8r58TFF$-=I%&<|%N2wA)n*N6 zt=Mcn9+Ap(I}mrjM}XFj$BPg|B!fh`=)~sAt^KW6h!8-cQ1~iNw5Rf4>Vt{1A5B$k z**p=42+Sb>N3vbz+_4IWptS*UnNiP+4FQuWvu>@@IJNj7A?30Xw;3AnMhfdA{5mQ3 z#tEKUG|=Bv`}{#O0nNO1p>U`--fE1BRV=SxH4n81}XtdM|wMdvil8dJ&a zlE)M&(rO6(m@I#|*X_WhwFc`*0DOF}-|?6vj^&!WuI}wmfun*7Hz%fkKG-S=v8+TR z_QU6D7UvLYuqT$9>?F}ZtdtvcSg7nFns!8v&(3o6z(h$H?U6hpqqvSyb2A^VRr3H>6W4XY_u#AKbb)m5n7Z}v+BIXIAzfZC= zv|PwQ`<<2U`Cx+BwuK;!nbd&p;zfu17(1e9^ExsOVHdNwZqVeKS83BE46`{$+z2^h zJrB3h*C1B}(;SQ8*3tWhk-phGT(A$S@-ENSI!sirJ$Q>vgy2lP#bvG_7xNSE1MzTE zqlNu%5Q_#(0-l@TnwIgvhEIk6ifS+N1aEyq@*bE5m~9AKQVTXo-rN-#sDZ6+6kP3V zIg8Pz1;E)Wi1@P}(D4bCm_B<_#zGDjY!!5&{?x+_^9 z>qIg~JOKo=&Rh4R3W-At1tepcQ!>|$74kaD@32VluQ9mWMDW@B?RfiR7u05a?)iI9nfA(Zqm_`#-v(#9_QOF4W3JXHy zN*IIjtS{#XByWKM=yhQhJNj(0E`f^&=-Jc56O>Y6$%3WvJrtZXn7yXiv)aWcca&`q z4F?{C%KeR(6}p~`ivB|kG>79*B-m65OKXvV;vDUVwJ<&F!CUXWsS+l7pw z!6dLbv@C$S)voz7E-o;DEcfy;dc|yezbDabS0Y*x1GDpUewSapC+#+(`v_PedTHE$ zX|J`rEsVtCH_pF->#m%@%;B8>+%~D@H`H<4;wbRrkUVgm1Jt>NP2LG{E$fXtSagl# zvnGiE9IiNrwMO=*e4z?t68Wq03a`U3KqPrX>6i23!6j!L5Wt7nL)JJX3tz$<)*~rn z^8^Wrw3(OhdW$spo}bsshgF3qZ1z6KC(34Ypj=)PXlw747*L(udMFzgIlu&pOLeSSZIz!@i zIZ2R(6@XbaDT(h3l5Jp%x=$yv4FSPSYaaVt{dL8;jpayn%h|&f0^b-Y`#oM8qeGbW z`au5|yunKw`G~PcA;$8vKEPt=$N-B}_z5nfa6n65KR~BbX8<_;DT}u%Lzl|2#rh5? zn?bT*(*w;imx!kw+CostA#9e_L^`OmGp^X2_d`RlLbBI)tR~e(?i*(u22?r_CwExg z-3;GH$5QVl`YbklbHrE1)Zh6;$cl9<$Pl%*Xe02Jp!Iw{{^bJ!nm4qT!MCjs>P5v> zM}5ZZ35#y^-FUw^3sE;DNRWGVe>WPp_m>n4B2@?`4>Q*rFVnkzsuAPX!hUmKD~tCX zm}n|-sm*A=%$OaRcvvDtvYhrvIZ)s^Vd9LO?HXXsPUM14S=Kz{tSxpDPbX zCuaf_3(b}`aVMXxm>l=)__n$k$^cq!Fjc7%Q7pPq115m_Ute+Q{ zD3GQ)_nTwKt~9lCa?9#T`bd1bjlIdR^W9RkAp}*06(KEG948H#B>V%b3Veo@45a>5 z4l4P^3Q1~!L?Ja)4CJX*&Pb*(pX#Z^3QK#iL8D+eZH1Zj7bU=P(H}Ayv~Qi~C=QFy zel%Z_K(cjS4LD`k9XN9k7FdSr5*WdXZlE4qQw~GlhnkheZ^2vmdb_@<1j(@y78o|8 zlBVBvcH#^I>@Wz8iJKV&Ld3EELx1*u#2?Z?K%iv51+YSUphyu;6SE2O%CIX0(KO=; zqSR?>f1%B~mwlxla!X@208)OI#f%Jwq+VENJsZ>!L*MLdu(6L(M}y5=kOBI$A#yIz zQ`>12XB|Yr9&%e7YZOg3r(MttNDVMZ!Nm}EP=C5USfVI|T35;y8o=SgBLCtNYfz>_ zA>Y7gLDv9hxP;|F@Bz@2mBB7VA00J1!eGAN=|Ewe5u~0J7>$|~C4i!~j0iP41xG3X ztC|cZ(+{Wt$hzw7SQ&B}l{s%$-!TI{^xG22&!_J4P(_^Mxn`Hl50xG^ed z4$8+MK1eO!t=C!|#V@2@CuR8V{M zyS*OnEh%7&rs3%CmjyyO(a}3TF2fj8?9X0*GEYj~c&%?;IlAWm$yR^POJvT=2P3P&0PKtfHlHHH=}KYwVHc;JSfe-M`L8#e=(Ify zdudLH%Cu6(3WBeU) zQQsLeg1(UlJGp$S$;am>O=8bUL_+z%aMP@ol&@|KY3w>*FE{TWUxzoA-?lB57j?IF zD<4B6mzN)pSc7`r9K{_Q`wARdd&?{=73Y?bPN!u-XeF&h_Ul;_!9f0HxDwWjCrXBa zbAGWxf@0s%d8;$dq#Jv4Fc$MqtYY+T%Zm?k`8iQim|msp)+(C?4v!c)RVfbhG#<9i z2}ij&N|B{VmR0A(AHZC97A%^_%}@S9V=Dp&-(np30f>e4#um)c_R@Xq>f$M*T$!$= zCN3%sDMgpziNlv+8*O3T$=?vrCz3G})@_k&@NVC4zIBn7o)XM0J5FS)`r_y8Rk6wH zQ`RRNic1`m*N1Kn5{c1RiB_>tM%2;Gx;9>kJe~ORGo=s#^LZ#>k_K!Mfkq=v(Sb(m8;j%9Wc1)X(#n~7g`ARsp=b0eiHQhe&bUkzQ3G<%5gLwJ>y)upR zKP0YHlIvJ8*Jxre-Kl&rKt`jH# z#EgAD|5;IuiH{yPAMOn=rFW^jtQ6Y4b~v5c&GqqfdzmSjXFlj?K#$tk8KU`XR-jZ{ z_V?PB@pDCS;r-6-uD2su&@T;N%$9@*z>v*{C$9J(B2m?qFHMO{eQ)vU&0DZNp6j@`}7FBcNSlNk`iLj4@VTr4n1vyF2#Yxi|LO#^9dw-I7xKW#%Gh?4PXL<2n2iNHzPs4PJ6>D%#akl>rp?v z0u9h=iwb~r7ge+bN83uN_VEdEgSSr3oFGutbbZ&997-owW_Y4Tr$N3OWd~i8s_N;5 z^m6ZFZP^G(c?p~jVz5_vQ?UqUcYk$HKWy?ZYC8Bez-o{KhMx_%AWjfm(NZzJ(b|(h zcCK^CT<)>Qu0f7`;L2rp-1E!njDd@Mz{=X-^S3!X^(GId_n(BQJ!8on)*H~dQ_ZHX zpHL!Uk9CpQvX_zw0vO68WLc18G9uQ92#|)e(1Ln0A~6|bsA_8>ZVfv|&%JLZLo#Ij zR@25Pk1-j6Z}~IdbCTyko1?VqTCwW`^G2|1La1$yMe&xnZ{ui~ozo z&C?=QxQiANC0eAY4mWuK?3~mkE-*OV9V=8FBLzjXs`X;8Pz>Ot;K<{ z!m011eK1&o@L55n0|F;}maB3W?cZJtCO45Yy*ZaMM}UGMWY$|@MDjojmD?lb#z}GI ztfB_E^RFukd^OL!$KSF=`TedXuKxRx3QKe(TUkG>7HDI-R!jV+zyJ3AvuaOrx4csJ zQBmrinJlelHP2K%+OZ+t;KLcVO$lACC_q*25QMjJ1}bPa?0XeXMAFz`cM_q^nKVu+ zYgM>)N>Fc!e%KDD%f*|Tf$LKaeoitNw|D|Y%TVgI7e z8tpb~`Y`6ZwC4hPsB73qW6bl3BucE?7!~ZJOl&Fa8;re_U(a)IV}|A*iV?8mdLrnr zQ{UFR#c!i;zr|=j&<=~1xPnxU>ki4~Yi2YC3pUnI;9h^vo&qh$QT$6b1cK{xlLj~h z4@fGu=$Sx!L*m*vWBF4gzli0YB8P|VIS~-gV)3qFHFt!la*1VLPcBSequ=I%DkrRb z!(?$W&NP4(fTh};y6lm}s1(IR=`i$^nn-04F!8=wuYF?LA(|R6!|7hp$_SRe0Iz3wbSu&Yvi5=3>b6H(x5Y?6O1( zArwO%woibRu+hWv3C@~0l^NAw|8S1G&JS~80z{%DBJluk@k_aP{6#o}Yo7Hs*@1-< zcoG2Pz}@m0bP_^{{k||M@%iGZ66IYI^!P;!(X9 zTi#Kyyk8a3PqO{pL*$LzC^>#$%oF}ZHN!`4(N*MF{16v`LjZ!;A-aDo(IY-A@j(W1 z?>X3yAmf!Y!!IQ>8TpDJ$sr5JH_O54qrxV(QT7i0^ra}m0H@2BLeN#AK7_s)ZET>` zJHSgYfYb~84+k3BkHN@U>khSqpo382%at&u%H72tZY~FEA{el`qJtPze9|#d!4KcAP zUq)JRWzp>}j$LDIE&(%9llL#wllyjyg@r$9$N`(?JcwJ_{@I+YZTYl0;b%A9Bg>i7 z#p1Q(ulae8XFmA~{&XZ|2Z?fod^J@rl28CFGcNb<>31lrK6I`9sO6SfemZDctV9QA zPO^Xi^Dyy{#Y}4?>*j@WK;t9rS2Jy{#Y=7;i8zJ^hi1e#dP%T>gV~g{4P+IqhAa$7 zwPZ}h{BbN)n}UFXxljPf$o}6_^QRTgK{iq?z@ali-!#>s8IdPiz}%I{=HL=bQ*^Hu zqo%HJcwzn4(+yJme14;W{0a4|z|CX?w!#d5XgR@F;9U^WZiljt!B#-)#tFu>Cf&fpV$unNSh3>$4@F&RdNs;K@(z`*n89X&?wXINId zu(k^<6a@XUI|GJxrzYJDKe*NHzwm}A>)zTX7CTqlRKb{aJFBVeEEEgXAX78Ov7waUstaa^f$o?dP-ZxdY)#kFU8JAET9X=pQ+HcpPC#37dXl0^y|> z!yU&8;Iqe%%TO^A2j=I?GiWTVKO-%vx%mO1VX7GqxsE*toI_k3zCv~7vgPuWu3)P~ z57s@Q#45yh38X<`Jr-%8jDQ9kUAx0!p9)?}*tX@x2p|M3(;GS5k_Yx~9 z%M@9(4EUVGureq?2c%tTh)~8bwkbJJ&g_2#_@+h^Is3KHNl3UGRsLWD`JNt!-Y=4O zJ_Ypt++8y+&+*XzCb{tP`td6~`61Em_4RnT335-rh0g4Be_8)%>F=VW+Aw_`h>%}@ z^+lt$yy~2R%r*O^Ooz48{lh~t%q-^4QJ4V}LQF*+c3fTTp54UqGd|ewn4V^WCgIP1 zhdS~S4%iqnO4gElb5L|h3S5NlO0$d5@669;I)XdpWlcqVje%)x8>Hq!cYdSKfXZgw zL(gUB{TUCoZJ87>=aor!*-xU^iFrjKO&KIf8D*VlYk{?9Ai}^w0e1{6vs9+JPVrYC zC5M~$$gs5-BuS8D#D=Vrox2VRa%Lyv5g+WRZS7EghUE9@r-NZO*~Gns2swfeE`Ob2 z*1rI8cPot9lBS_|qf1IF?4R}N8nT(6dP_`%!Fx>ky#2Hc-&5yy5YG-3i=7$iXbtcg zF58gGfJ2e5g*n#!iH<-xudlKELPHplBd6hi$p)wXvp3s}YGV{t_=7@6!eR#lwdVzP zlBKdObGudWUI;LTCE=@%mRJ(3JFxZ{Sk!XOT-m@It@HIiivmuD{{s1BV&eGU_s=yX-L}U6(?8#b=*3|09AIOg)0kZ1IooJ3{@i~6 zBoaV`gtV`hQ1kWay`)ko2AMb#LC;^?{H&^t?Y5$S{PBIfI+?g)PI9M$dZJL@J(-kl zNz=j>V|$^KzN`B6NWkpdiQckA#4!4L(tG=T_&RLcXKtzat*GnV*Kui@&(O0m-WkEp z{i+u3d2@aLykbVhXKHl7QermbV=EISPmp-I_w)34JcFc-vxDcsg9&M=EE1-^D;@cLU zqPK81*N_^URo>Pm+ftKRfN|M+;bCunkvCCmaE*njH&yFPk=ba!WKv)9vLBwtqoY=3 zxD6w7T)G~ef>0X=Y4YQUXw`RKvR3Qf`GFS?Szwi~l0J6WUxI8V9*B0FCGw=OmE4l_ zg9^X9I@E~^ev2}L%dTbw-iqsXX7c#lSDBHa8A-z& ztt&@4K&MSxoj2Q{;QZZH@Bt+rHlGlm^KunBazD4NsW@hSa~sW#$>Gj^&FZ}mG_12C zu*OI|l7kNKwU-2<=v?XNSY8AYK4`i?7xIb}YI`|WH@l(h`Pfzs5E=X|TkU}p9+ZGt zeq(-J1epnx%Vi!#12kPaLl#XdUWx04ezdCeru9~)tl_tG=zT{Gc$*El6=BuC5sADz zOcbK33$jmw-Yxav4jy8N4z}Qn z-O%VNFE2Ou(Hm<^KwObbh(Rs&z}U)WPy>=L4;ngFLSX}S2kb{ZJXrsZ51c2gYF54> zaASivNWHP#76GZpTuu5bc}0O1d=&<(1`JN)$n6H=&AN7IqnI_V+K>W)P(znUbL8F{ z$%1WkE3#=^NscP;QUG&!i^P?tLQ;euqr!8zsM_^*Xt=U7$Mw0Zp=wJJP2X*WGrK3{ z;seY6MVS#to)LzVP4&K@D%E99613o}mR8D^(Ye5Zv6*gtp2zN^06Pt*ogePGNtXKr z2uk#7wU;eZ6@|FIe+o=&vQfnVqe#<;T4=5RlAo0U#F9F&Jeo;F4KFsj zGyubJQ!=jT+|(9mr`hrK~3d~rxtg$0*X@4j&^2|CI0ocirq=&PpM{_PSRzRNeRi=`?d<{8} zbQXzB;$Oac(sdpyvDjaJH}|!WdbhBGC8Co0lr3K9N1V zsoIO_t7}pBD;lVTj#v-qNE|0tfQ0Lib}U)fwlQtHE2R0ibi3@X?hhhwW)N1SjfU0} zqf)gkg+2sjXtxuJrMUD~Z6mKr0?1m?Ijf#DK3L zIv&}dce}{(Js#NEK5x?fhE4J9n=CAvzH!u=Xq0-;k>=F(k07%Ey8Ah21!Sn@j7`tUwsQ=16TssxIuqvW9P|o1+h~G!Zlx*rde&% zpah+VwH>E~N^GDTX76{4UKrtT zZ3O=#+%cQzhO*aV@!$`y0k>xjaS4c2|9~Un;j$>qbA4w7v1KJKyfl-_g}O+9F8V_? zwD1S>tg}QUmJ!06!Mdo%{?TY7fwJ&~_r2w+VdZU-;m$q-vT;!FZukCuP$59rPZhAb znk35_cN!hY$=(gy_Ti6uKfMq;N0NwX-Pm@x@}%h$n#5EG7pvxHWzj|$if$P9B!9GP44m*uR|h4zxu#ysjQp{>aKZH@Id3@dLT8aT3?Org>$ zyBq+fxOP^j?JU#rMfD!x`U=?{Pawm)H_S@){Gz}zk0W*f#DUiV)>g{R&k^jKkV)9TwG5PTNahbMBoIC2iN;A0UCC71?H9xk_t7#UK0VNgd@c&dm zyE1%-G`CNy=J#^WO!jEWs53gFYTPS4#c_~Y+fFkYNz_bV6LS%{W0M@MNG8hA*40k9 z@=vBU12lDff0W;Yv@xdg3qZzbbmW(F7+Z2dZ2PgXHbEK#o~tj{GJf*V1#7Hga-MB^ zTxz|mwIU95(qFJ~Q);NSVUTXueR9im0Ci+=J!+(!b^o_jBkWW!xk5Fxj1U&B zjVjgL&f(2gm5yo8l|%LPNsd@w3`xJu^w7597A1~Y+lKXz3Luf5To9P)JI!BXRe)-~ zy7-hZlXJSmJ6KDOuAH$M4I+h4A#(JDi%h1kdXl)D9i~$mB+53buG`c)=dGG=QPdKY zpT-)0qNOJfgdXEih|0~g0u8k*N8m7zmg>OSZ`*Fc=zxj&+~9(*JD1*SeJYFh*6+PF zb~C-%d-s1WVY43(DG^JtVKre%o_#jv?S^XE=L96>blNLlX0^+esLM|+#tA58|11vZ~cWlO+b?tY-C^?Z@gJhVd{(QZWrR^5c@6L%!%HlX@y=>d#Y0nn zWYbd!G=!TC$7pn+BBcn(gf*2_b8~(K_*J-3O28y{RcLTJ^3!T0(O&6_5`6P2IbQ zm@t_~6VvK+bL4CHzPaQwRM-Y8b4DlSIiC>j#X5X1vlUbNx1IRaxwpjpy(mVg zP$h^VH4uvRF*d-erZU~#xC6`+ogj00RhKD(A6*W{UJ?T7ILEpmya84c-nz?vz@rAh z)iqLvO5Gx}-aFg#{G^*SuWis<>&vG5tf6RX55>`ZQ{REvtd(jSs3sGuvqRtnj0zOm zLdA+vLT~ej!bFU-wJMU@m~NE*V!eN$8KJEA7$i_?@I41ZlN?{!GPKfGqr2@1!2|ix zJ+-%L*-Tnkr@faIYMqtnN%`E=HHA}Pyq%3%4k5{9SCIFd7aBpVFUG>$krufbD;Zem zS>SbNTA;*JKX43atOxgmU9w(tLGPI>j4(l84*Y0w7_`f?!SKX5Tn|&RH6l`PoXf+q z+vMzd!?EfvQ7B{;-~ORJ+Y_SJpl`JuLogiORR}8cF=&t^>gn2f6h3Y4-_kx2c2T%{8(1#uYA3n_>6J)}rAqfom&Qi_>d*QP|34Q-nM9e zvKQaz-W_E6O|E(Bx!F!*Yzp}Pbn&&02&$J&W}Y3y;{H*~+H%Q|U3*X)fTh@idJ|p+ zv=Wq?oTBu0je;yTF6X-N2S~D4z0*ZVctb;3f?aob6F`T!HD<+PULF+79zQ z6ppj%cIS+oi?k0pv_fCp>veVMCF2xW0B0jSXS5UWK&O>1w&AMG^ArIW)cePVAmEC@ z8S;^@y&6LbPd(nf;A@42}bRB+Z<~mm1@T) zb3f*Ech*Nwj}s$K=a}sVXia}(oGx1cA;BWs^5gd%)Qn~qxxRM4L~*?uD(DbcyX~RM z9$foY}3P*lR@QTa_ut3q#}LHfBONENUY+bTgu(ATA>wagtv z2=@1&G0`zm-*HJ)`#H+~o%{FM{51>g*&gBSnbHx!QkrnRU91FJYmO$Gtxpw9KR$fE zS}QO~mfDZOd}O$gB~P<@BaiZ}E8BWh4>60Q!XQ zOFV**?{_zhv8b}|?(bV=Gn(i7s;r4gf9t0i%+cHurZ8TuC^19+1(U9lH6%xnfOx(Q z9H9YJ$NG_3yqeyYaB?-sCb|VKe+(tUB#+)Ud<_=ywSDT9*PS(Zq_z*qqRm@R)De!;M0P_25PSjzOi|gApQ4F17b_~ zhCUb;P-|^qGymJAUXZ~EvpQx&5A5GKR&8JlL!K@ucScB15e!N>UL_b&@HxHj4T;`EuZcO78YB5?5fkk559YUtjB**qyM-e z3$274lPi|uh&uV72TP6mU!|M=K=(c`6w8}Ie7`|ORN zYQx5aq(Qv6`}nF13K~5kQOmZK932uQV6#dAAkx{HyRb-4-nWy(!;|W7xMJc$bPS!X zSZG{ya3WTsvo-%zf~cOrV}=i&F?tb90vjz<#`5MXct1#9#!R9kwi$r2Id6c9QtPM#B9sT86pGh(QZg8bq^3o~ z*3yP=c%-e2OF^RGKwVh@4=L-Lggg_N2_N~Q8VEU&K|r-cjTa+ilm)47Bdz3k5Hna? z?6WVjhY@E1)UAGedse8~z%!G0cBqkNH5m4ivw7~uF|T8Ms|~qR3~)w8Nf79%_C}pZ z7TEKoGXCp-EnxVQR;&*-seS68X-iuv_b!ZUo7;%pdo@v^_q6D(xRole_yNH|R^GYj z6NFa_zfQWQUNL6vlcBHHIYur5q}=z=&3uB{@~hr?`f{cp2C|497#Fyn+VF! z*n9DVTCv-yR`oNa@mG3s>dsXCH9me#6i3qBsu=0q^~X$ZM_Aq&;y`HF_4Uq3)2E9c zub{g-DAl4QK|d6iNt(d^4N~XKnz;?^DWJ%>Coo?W7#U>%UzwcXTXuYi)AzreYlg$v zuG4gjHsx-I0+x&^pQ4767$iZ<*zu_b9F^^}g zA1;Wigq<7EX1|syma~B&so%llRh5 zWA`J7nzOB9D;r1wAPoQlF7sh~0K0{zcf++O)|qd!RU*-Kv-ksE(hO%Zqahq!%^X;- z%0dhq(NsT+A^{c=YfqVgO}vgbhAnVAXexkoha~UDwFPjPD8L)m{zGxNXc$)oM?g2i zv9Mv_Ao|LKb&_hQC`Uj8f1ss;2#U>er)zCP%tkQySUnpYZ_L=wX=Ta4{wI}q8u#w088^IZo>HXIfOZ^lMlKWXFA{Yc*KZN zGzdVW#Z#N~YM9`nksLQ^2qQG+*q%eAFi&U%KY>V6&8&4n2$V4mawB&OLY&t-B{-XJ8}Y zwgA9X`&};kWy_BxpxuJc8`}Qzu>f#b`F-x+UWkty<2V)941@{O#1JqOw1v~c5aNaH z78?tG`EYya8?_A3JciRH{Iy3hh8H-NZj3*7^eWL9w$EU?A@1~+(|9fD4rB4+I}>kbUeD+7@j{%X69wM3?Q?@SUIzz zd$sx+t>~GK!3q%P63vw<{!`8q`fm~TZ;?6_#3%)iTok&`I9l#?KhDK7DSqY4&GsdK2E-lVY#x19d8;iML6?Byn?bH5d7>|J#5mN2C!MncTGEa0 zg6^>`GQxh^RaK$G!3`r4_`Y`@#!(MqvoW?LnVN`Kc*0VL^>_Mtbo~QW)+VMy?RNn&+XPRYu`K5_P_Ca0-zWiR(6`1AM zG-^nJ!YHsWymdy-JXUe>^>6iFsHZiPWVuQX#fD~a8&u=&nZ# z>ttQT9h6P)f}TNB5vmmdhtRM0BkM>J-@mi>9>aF;fcr81UcZO-ymQLG-;J;OcA- zCYgI3f#C{p%XjGV@|;08rlDP|)yT{B&X+LVq6h`nJ=$eZG&Bcb$5qaQtmgh^WCv8v z;~aq>aH%11XkrYQr3`pn0Oh5rWN@XD0LG>*=UXaT|LV*7YA3_%fFn7D$bFx&oJj2Fiq>f|kg2n`R$L{9%`IEAW9x6L`$&^M6rm1S8k>1Ur$@ z0aG>_E0>+*jE87SZwB#F3aO>lQO);)Z58P^6My=f)8E0cT6?fkv8jg{b$8A%(uala zQCWx`_QDgwsB`b{;pe2Jv|gi1^{n)lgNgVAOy3$>lup~VDs(vrCO^JjOc$WV-j3QB zAgmHCMw8m^==S(sOuPhoP$WAt4~6>8A5T$PC}>dEYW-y`iG_Qbk5J{G%vbxk@JwC6 zV_>vfDJ%J>FX18rQmO_pOi;3?7}S4t_kgd<{oMS$!Njk4R@H0%#tt*dj63{Jm-YT2 zd~QPG&|R`f%gy=mdL3Lz82Ec}LB}yBMSu4h`N=!FwL#H!tR z)_0}h@m6-`5999FC>UdQgKrYWR;CDOIyw-(*?L7)O*$u3a>iDn?<(tKKLM|N`LIZV z3;@gFJM5@*#ZJ6_;_^Ps4>D+xWq%$kRDw&S6P7pdARVHw8@~ZYP@i>KcX1z`3z#h-g zmFGt1@T?rhZ(c8Zi3`=-CZK3o!6g-0tDi=GhD~>uetW%9f7W^btFdxjBE`90eJ<59 zJ!A&xCB0{IuZnNrTutFRvLi%pCYr+kur-8W_F+N}QRkuQGk}>Pp{92wcBihis`DMJ z8CAH^@ZyC@-ui4ax`ta-u+sWGAPJ9yQIy44MbEu&zR>xk;;fX~@!M>Q*B8}J{q*dT zAf$Gb;*UQZ9#h9Fr>FBA1;Zp*}icAyX-CCt+H| z{_5`eGFA7DB%|{($@VoVomjckd1xrCTEBJQ5O1YhH|&E!=$0bp>T=3(BT7kVp=Ovj z%L2_cR42ukJmRf$(#wtGQZsY}hbrVL@kg)Udxy}oXk8Gs(sWu5m5S{30wLY}YTie2 z2tz*i+qqXc!PH9eby3x|4UcxkMs)WsADAXo%WXEn;=PUQOuzmZqX5r&cHrIr&U+voW3};CHIBb>^YyfADldJqS*9j(wE!)XDf5&H*5n|PSc44IRGusHG z_#nF|%K4RZj6mXuQyl!_dyX@<%wVeuFn9z0Fx&|)p0Maq@C>KkukeorLahe{Y=y%d zEz9*EgSZ=lPcji^I3XF`Zf>vNzaw|;^y->T7icr3U3bbgzknrRcVZH zt3Mkn5^_OIcAjqpf@3+|Xd7ALPQ*%oYMr+UXz!*zd|QeR8gDiG;eOXD}hGIur+d(qnUpf}KrVTl}ajLHl_iNn`#g_O{XxWnT3o7QL zU@IZ814N6a`*&AY>IDOa*NSNEZ^NFRoNQ|>7IvOcHN=l&2U(zvm~0@ibL`0m*y^Ts z6lme!=Ch|)oBTal+tOlFX)k3JUpX}fv7i?}z^w<+!n9_=FH}9I3O5_i2T!OZRqkhy z)+>JsFmLhGqu>Csy&)(Bqjot+`~Gles%WM z>f3BOb+t}1d(vb>H|y{xb+di-V?;)knN_gSvh3KNxUZ&c-py`Pf+u{2_Wm%*==AT@ z^CsQ8pVTlUHVF>>834&L;N2kfzBKTnUM=!<3e!WqmL+%13lFTZ;WF*E+H~l#Z8QY6 z-n32Gna}gPz5QcD5j{wYkSypcJs0G5fPmC>mNzp&&mi zFqb`00FbaXAWPN8y@=77H7I|;$C1pCID*wp$5LaEa0T2kc6wz>JXtPz{pC<1m}VD3 zQo7@1<9*oDKNkQP`etfyb)Oy_0sb{J*c!(}*eAJ^W@VxTbwUlyxFZX#X_g6 zY(-$q{)T2SzI%`GK^bf9;jc?ox_zi4l&Z5EWYJyy2V%H?iTG@3u~;zRluQwx33(5Y zqiB>Lm;-PZFnfU+d<-X@c=-@d2VHzqNR9|6jtI&q>tU77w;R$AmcIeJV6vion=wKj zTvxMo-*OG@s_`fvdMIv+1M<-lp;T%7@4twrs3BcaK+q)Ma2d4AX?KNrSI^t@W@uTY z7#Yy=71YM385ML7$NZGhRQwQCV`cXRBeoEZ62-}YkwciksZPS0cfh9eVp#Zx2!SHY z<8~ZT1QOBEKKp6hHilA-N1f&>e&oMP>M;69=%p51w=qVm(XfpS3mIlX=?Bqpbg6K< zSc>OqC97QRk~F>28JpC<3cLI=Ew1ERPr4IY;Y&pQ!pLJAD7e{mets^Ed&tk{f%?Eh z&Z-Lq0(TX~`+zkrP1F+;`LYe_Ug`ZwaNp!6zyHEV^wZ%y*AMkNcy9PblKnx<2SOR* z#bUj`l;5b6#;h_5oCKyS1@_$PS~L(++E*JnuKlZkEc_~7yE6`ou&!U>v!}X^DjsD zn0$RCawT>~&yQKWw^$8eX9`2a8RSghdF48Qs||cv6KtnEG966wGGVqnD;G=4&gD@7 z4okFuxX<*RwYIaf@Jn)V5vv_VHOwM0TRpSQmE+&pll;-Ie^w)XpxK=^3k-2#csHJ? zW%TWnWzLF>PR&DPMcUiZ9T@p){aTazJ!!U#_c2ngI>xA0BAbLeer0fdEF3a!I3HSm zYOmw!NihhxZ*T(EB6sj`ZO>hVSlm5nRbM5Qc;;_u#>i zZ9kPNA*V8sURPE8*b35NEW$W{_;iUk`|Z~tUccV$7AsM304`Bv#{gHz7{)yDya04; zk9GSRN_>aEQF!vEpF>`AbzN&{M87TWj?S44uAI+qq$Tu8=fRk)~D` zjWNhN8iJh|z>q45FxVptma4zj#NcP_fKOmN;*C5H6nL!;gZd{BbMGW0XwZG|XFWw4 z%&-Hex~;k0$k|X@gBJykP8@`r&@I{y?IS{Hi5LIb>Etfc{vcIoaGZY#cVw`?W~@bh zIA#Jx*)W*WfzL4jH4v+pVqrzcTj$F)(5?}tX@#Laq#7mYAmWgLC5|1*d>!*(?7!mI zL(%|=e^fH5V5F#lPuP)=oXCNwa5g59tbzaq5ssAj85RJ9OliUagfwJ`eIy00s5PCi zNvFjiNG7&;zxE*w2^d8E!lH-xNaQG-XKqw$I<6+q8b}oid>T!3usaq^p3WK) zB7}@~Oqa+5p#hmnt8C3sWvt70POXci!xX#^Rj!Vm6|A(=yXOi~;iwcmJIn{h;>Em% zDxet9x#rG(@(0fTf+0po{Y?MO?L^5Mu)DqM5$T?)9Dl)1)j-Lgu3fgO?Rhv)m{TZxRJwCL&MV>x5fzk z>`nYZss_>rtG}?(R{;@SIYv+w$>^Hm08ER_HXy^JJujwW&{)vgEce-kl+%*_)I6l&Ue=0kQGSb zK3aA6XjLsyr&~YfaUuj7xv?OKRHsN#Q%3;GndmgT=0Ons_m!%I0?+{4$`VAu0DqMTCi;FC99tEGio>?$yx0ET8!ru z6_JbL0xAVaIUx&Z!tAg4iF=F%%<*vFl`Q3Bjv4!xM1NKKP4g-mby6av_ zx3=ifX({TZ`&Bo4iZ*3{9El^(QrE&dR7@brU9v{civDPd{4}ei zgB@B2cd`)nCP*cAcVH(nql^04hO`khw2$5@b(FlYu0l%_unO-L4*?GvVM-gI2r#oK zH;%84^Kj%|VkF~VX9<)IoRvJN3}(GHLS%GxAL^2sWgEcl;`$G}l*T{yTcgBuqY>l) ze#iM>X1dAAIFf}l{zc4xo4mdu*Fkp}_pK}Yv&v{X3eMYgmJi&hm8ACApaCP~WIiqL zwhbQa(k{&zF8WuC9|xphX7h0F!9Vl-FA$jAPMAE*wLk5ro2Yl4?tk+hGK%!q7Xe-tF%MeTp3jLz)_`*Pp!N4#*R2ZRJpX#}mb(|)0^F)bt8dpS4mlDuZLRfsF3+h7QrQNGj4>7T~W z|CH~YVw&THOOo`g2E+B3#1HfUezR?bSuKFf2~~Tw`{QaS%A14^!z!gBpu5F#TRUa% z&X|5YZNgZV*K`$>nM=I&!dvKrpp&XCUQ^$vdpdaK+>?ojrya%0UP<4jM8kSM21@)m z&*kq{x(s3P7eTNGylG_6g8wK+7@7X}VuXR6iT(e2?xQ*3xWW2o>w~JTxReO}M$FZg z^qTmfShmD8$(TSYd4Ph1gcxPTZzn3uSTWEzX|E6(Rsx{wWWUEA0E+AdQue#;)yLDp zW6D^No^dtaxI_An%%2korvz!Vv6III@kVDQYqd4=Npz{5p8J*FZCTwozBRAhZyl4P zSJDrTLe8m3dRxaME=3Iz(3f`GVTLHi`dORR86SmJ1W?k+)dbASRQ zt3+#mh$@rtVh<1F`_*S$OAu?gUZ70T)I+9q;eRB+$ohDSAUuGI-lVC{nou z`d_AovEyAm&47NY_!I)eaW)M46hb+!FuPuhjC7r_poRgI!dQ1g>c&$S>c}|q7{ZY= zzVT23`#+tv+w!;oiqi1e0DlS;Xp6cuc5Nfl@~k zIDIul<&Xqyhz0UA2b;+u22wMiI|KUmV9f#xET;z?MwPOMm`-X@;;GmaglNGO1)fEA zS*xjzVrOb9a=?Y(WgrA1#y`B|RGC5+fmA4qywKD2R;@Fa6*`jXGvA^lX*(q#52tHmlDZv9_0$xM=U;yd15UG>dAo*oZi!pOg%RvGG`c=tH7 zarb0XzRpR2%|Kg0)deF7wYbYb7QR3a8j~N1Bq+i;w(=sv+C$Ump?j3YVh(Wh+Oaxm zd5Wf{bFE&`*Y>0KE`?yw*9#Oyy&qUQitw-A}W9vjECq-Ax zH9iSURjxy8MG%o43N(gzht9Y0sUO;x{K2#Kz_d0=Wcr#X*CjxZ6+P!>fYC>lC@9?2 zD?~J~II3J0KlZ#^mNn8@^7fUsR!6v`O5-Wc?r-mupjMCW;X{Vnx{L9*DI8*g=Fa=b zRKt=T98GmsNJ=Apw3E|z2g$)S2R;=dG1ySw8NG2kg&G%4=6+5LJ_ZWG)g#2YP|cAl zRgLxF%mg<8wcyUWUKuNgshh|#9t+Z_iKRS1=5bcTWF-Qge&|!T-OKa-v=gG6JW2c< zog?_-`c_+57kQw2ZH-@fbw(p+dC#Moow59`SNq-Y)cAVlJ;SL=h{F*pOcUVgrD(H& zbVbYFu?Q;_&)*II%X@-aMk^TF`Km&5=U1(CzoT}&O#op4FkcORMTlD+{?vd#PL~Fe z+1-IsxGMtkJ-9?4U9>peh$+sEiz7SPa?mCjg(a20yXaJYH|Xx=4VWSrR|H8Wiuh2E z;M;M~r)ttDMMvNWiPzy!M+A&Dkdb=boA2R= z7%+kXMH{(sIt&Z{u>$E8KWcOVz$(X#?+>jo(-w&j;9#4CnLtXGwOD(aFCvFPA7vHL zaJKQpl*tDO2pX6-M(aAT>|<_7Y09&uVTO{hsM7J_gmsd~2(Cu1Vs z{nR|THfvvWME@M>@O(QbGnw7u#y8s5 zofXe>`DK)4_7r8krEzvJ)s|A=TsFmS`we(>JCV-UT{8a7M;xsEU<5NML3B5s`~C2^ z{>4M$4bhNCkh%?EqG}VRiUmz3`RsgpY{^J?{ZalQUjL&UK8^c_1ups4V&@<0AGS@LHLr5smvnkTEG0gocG^jvG zD1H{Y0+uA6H}g54i{i zR_6cvHmCak2u=TwT>=D;1t8r-8S$^)UGr8Gd0;W?yij&f*S=?nWl0R~8v55m&Yrxc zO?4a1Y}C-+WIWS=9j+BFk0|&1TVU8tRcF+VoiM7zbfR$PsV!CsqonaLQcY1O;XA@l z(*%6Cm6}yb=4uV^_Ln4PW{C!+e2|!xkwzwIu{~XK&wK2l-T$oXw&0(?OWmB;SX&ad z%0!J6^fXaVFwXZ{hK7y*-D$B^$_=)wB>n9?I()g72yZ4oNshJkYIE*>ji0YJlj)4P z*KG&~GEH2WyA`HO5?5?pjoUV-MntetWegQ3ie1<`E=VN- zha~~GdCi1C(qikAJ@Ni46Kjc`=(yN0SP1&;aN;>@W6)14H#j(`A>I&e4KWneYQX!e zHa_Um9OjIzb*M4(*< z*j_n#az4_)*SH;j_XQ_dp@RD3=;I?(wRqZm$SpKJ{JX{BwIT$-?FPf4^;gMg?+*Ld zm2D#@Kh$_{6h*D*rj5z*UEz-4CJ+**%@;MbL_g14q*r+p7XEuJdHxPIYhKvAl$mgH zKnAN61xG)_pC8I%))3_E=DPl}R5)nxY>SP*ikV3=CSL>sGonnK=-Bt*x}F-F7p&$( zL0KZvXRkn^8WzkY%{Xk{iK}=lU50t};A)lDG#x=YOX9e;#0j6!Kj7{U32K}~#^@k< zaC^Le3T#Hk@BFLaGN|Zf^EPXzX6I9!8Zu6VknQMTHWc4j+CTf2F62mv0WNi#AfiCW z5u12%Y+uBSW8sXLaR_k^lp|rKsT@oChX}E=l+yeN;3ORAS3ei5)qE?sTR6@u!cRba zz>b?x(oYv-^wrXS@0r_shElVQLtZ85y{{&5HZJ{OpBvP63+6jj0&l#cF?#2sGfEEa z)s-750kmFOgi%JL)TL=n&--<>2*^ENe{!9e({s$Hc#kwt7G+Dft4I{opcQ~KLqC3` z5otiV4gwEnfh>R!bSCHHJ}*#jSk2Oy9P(Bn!=FA>WFav*9wS2kFMmjffBw8GvTXl* zC$yH<`n^U77F*h5cLy=plGdubSkp%!C~_1TDS3}(jIfxX2NzQV7Tk7lX$U63PM{2l zI6c?ENpbPjq{)CBEwB;$EG{XGZ|l@7QW-fQKV`P!%iyz0UmglS=onBs)TU4y(zm?B zr9BhyakMl{Dh(AkasZSH(L2JKMRX6^~{ezD`V)g;y5eeDkCqm2IRi z4u4z`**+Ha*Q&Do`{pTP9oq;QI_RjEJ>iay7k`Gcu0_I))}0b1v@F0TCwM(^M$-^n zuRs`{J(=&&_kUGjBRO<{5v2d%K-ZTWW7)xDbbv|mP#V5MmJw|~j83?#ZMsH+@hW?` zm1A9XGXed1nulXgyI?(W1To2igye(p-O1Gh?qFU76o3ZROc)ZBVgxh2o9IXL<9bjH zbS#BfRGspg;-zmZh8CrK$>bXw6apj3aCZ(WF0VcmwrYBT9^gxl- zRxG)nAZ*YNVr0$H7cnBk1M=doWWb3MjTiyDIuf3Uk54uh={S(G1|LA)=*TSw6HR=VYm>QaFI1#HTEN955dHlUB%AtXdFwaHVbNaNOpB526xI06VNS0 z?je-XRt|L{m)VqszZzb%UYVs0cB zwLNwJDgw4uPWgjCD0*aE%9fF`WJtDfDJeB~!Z*ci7}K-+e?nR178ex} zoo%`t>N~VTp!#*cPD~AjRnhAP2jqcjw6(3#Mr)>%-v^QFZd#-EdO9?GejVH4)i^T_ zE0R9_)>MSSk2V>WwH2`+hVAp3UU@%*J9@l&KTYqUSECh!wCzNzIPD_Rxok4CEtlHffNOuDSSf9?C|pWj1!b$sRGM<*^|gVs&3 zJw;a7HW5B`7;trl$2+Myd(1IB^VCKPJA-5Aw{6>jl09R@9~L@|mYjOm?$1YRs&CLA zfSDQ+f8uYaeaTG~Xloiw`uZ2rhq^pB0|#tJ7iQ#QEagtysjkyoP|M>xO{ z*Z{Uy0e~$OzR>;Uvkw>Vb*@b0Fv+O){_{J6K32hYaBQqV!){L?zp$)!GShLx>(E{UOs0XxXV zHyZq^vIJ1^ZtJ36^KUk@LDcJFDD-J@{%aQ9s{d&d334@?>3+#3VjmdxN^4HxoTW>7 zjxz;pw?~8WpuV1|*d0WsT*1K^CbBacd4`fM`a05b;BSLz^h_SFk70OuM0Ak#i^mR! zF}JRew{CzEBb2vrxxvn|7y{G6KP&Y&!4?I8d_*R#LP)Kqyv$WxPp*+v7aBh5=x{6wUm zUs-&eUl2f|**!i#-!Jgu7a!Ek>~|{V+aoZ})%huS9<`@6XUg!-m9EPWLVQ4@UNH#e zT0~vhL|MYVrDE9tdI_F>xkWXCI})Gg6J@oBz4Lu_?tjgsNuFFXd(0`T$QAZWL`Bc6 z!HW1oM=ST2J815Ec3r3O?*1HwcIrLUR%tEcZSEP>JD4+Y{JGxV-$iciEWBCMBU0&H zyz{p-=XNxj9T}J)2)cuGF8-pH0b>B~!yZwXI6ymskU?P&0pb`x8^g26QsxH+!n{oD zI>TNOARL#I)p(CCio{prbWpAnFII2570HQ_7UzWCs-Mm@b7oaum+N=Cux5tDo;8Y# zj`<6N44!|N*ciN5|1ONVWEO#&%-II=$SJ+vA~o(OSj$tO*KL~mZyjdZyp-Elw%PzQ zCsHL!orp@6=a}ztytBhXgsNF266|l&Mrhv&vr^wSA<7RyriUX)UQTP|th`{X1`eU;Nu*_Vw3!@Y!K=E3N=G1zvQ3*Eb z)p`6D17M9o%g}*oG-ui$ObG?W80I5oB9@tUws3$9UlPPoL#I>-e-RRa@v~+Ot;J}2 zd`?q|M_aAHDY-XrTP~>NmpZC-!)4Q;)tO`uBVqA|OJY)CKw@V1NS%7oncJeq>Ituj z$$>j-tEbW=W_*axU1~Nf1@8>NzZP1vG3St-W6^OV<$r{A&(M09$Kr?>wO}w9=RCJH z6;}n`QLQ{Qa5L;=uW(jU28b^en(uk-Jo2rKCxS9%RPgT>n7v&j$|6(}+`gb>DhIi? zINhGXqt?EN5B(4bt7vEM0%~NC7^s-+Bbt1yvB$`@?1 zW((y*fr({Px>E~+E%w0`o+B?73wT{}(x{`w1#YKg18;;NDMjz(728IkNfzOx=M}%u zN4EL|F0*Rz)JZ~IZ}7k7(#JP4NKiR|u_Tn6n7x)>=z$I`c0|%2phH&=1B1V!>8;bNHu5bmo4HT=?)(_ zBd~?=;TvEtn$YG9nCikBHvM%CB9O>)8r7FLu>a>uHujd<>#`7M&77z-)xM1$7&x|# z^Jg{lpf6JgaMPD>OZ)Q#s1f*93EKYFJuMbL1u|*K)9>!$rq2*qNFUD^tnVuT;l&?N zEeO3a+0w!1PLB3bj`BS9&tV7{Hm=RN(zYrI74V?N2}|9`8pyg5MZ8Rq%fyp|-eNcZ zI4oPZ4JDRJj!6x38-4f0FV^KGo_Nx%B#Ow1K7?9+jMbEeIACVb1gx*RRTQ)r?;NN#qwEAbD>XF3|U^Y=ToJ1U4SPq!(l`{`I^)T zYL5qjJR6)4qrKD#oF_ZMLWEfmOsZFa)9SN`N0ZILd=v}(v9jojAjRhCeXOAS7)F8$LLWmGn;>U^t2|J7tR`B?mWx(v+dAz`h zbDnDP8wH02#b_ZO4;`nP?e*EGyTGCn_a(a&j@)GhP5C7C-fB#4n4uKJqD7nOc}`|= zYQm{UuMI93@sLe_<(y*_<6d4_F%GLHjQI7)QXW{qip9AA_5cX31v~){Bw^lT&vX3> z@*?K7&Q6a&C;|cL`E(BWI|hv=@cJfLzKA_WMuw$D*XBSQq(nW4YX^flg}CdS9n=KT zF+?|t(Di~GG@L04nX^PE$n9jK-sPWtHB37*hxHTy%+{YilM|n>6!wRx{v(rPO2XHW zG5wm@8L}OYIZ#1=cl6@!ykQd8^1AIi4D80?VWDv3+aI9AS zQNp`!gK_^T`+|fOsUpMZ?tH68xBc$cz zj6=nkKWXMVWM2KAoU2XX;B$ng)G$3SLWiu(u=3bw`5-kq{$V&$yc|8t6@LcnqWkTHtcrGY0nA&WJr8UxTn8Zut{;Qhfh%;MEs$O*i9` zp>^N@vwfTymDabV>c);_d&~yPs9sD`S~nBAkTXtS#eluH=BJI0Ss!@1Gb}iG6~7g5 z;S$>gJDl|(x-h?28lf3@tPcre%O*}`$tz43Q2#k`pKs8kAzHmzGlDn?u7aDU9#pBGxR__FkquQr)uzr-^8fyye z>#tSzva3}tq39<0MoghhdZo-HN5Bj}0w7CORzQ$l&DOjm^>Z99tgb&y8uZ@JolW-V zdmRBH#p4zF*3kWgSLUGE{*R)O`9CJ9jP$H*|F=csl*X?;tSHj|Qo>RNE%M-VbfR{W zq8M|f){sa!02b+IY$X?j_Mbl9xnYA>1c6jk3?9g(&t6$sSom^zi8pn_nEH0PpLiNO zQ$LMX*e^F)dupA$o%|m$hqWi_kHnqm>d~s9^B!yz!SLVJxz{ge-=~(gJDUtY+wqPz za=+-dsR@xxw_K8lU@CHoykhQCWhI~V z`&}#0V0{lIxi7e3?!>&?!;z5XII8|B6m88MJsrQ-M_L8^QJI59Mxju!#3=d$=-$my z?}qH5;n&|?PjAJv`Z7)0@7TYcOU1d=JoPOm8ON=7M^Uzwd8y=H*3zcitv{|Gy5<5r5I!hNh_)CVqt<*w)hD)54QDb)(X8CBhyW`f% z@;BH@(5+W$l4kfQ+`Gxdn(^hEbBjqNoTzCtAu2o|&@fE}L6y%X#s}L+dEz4(YwjB@ zT{_95;#@d#*d8n)>!ZghV>F}Zdnm~b|yTw+*Du>Pshu%gAuBXwVwuxZL{)rNg<9}tCr)USXh#u z1~)SF5Y-}cnZo9#K98y+kb`1g>%%w^CoMHypHGq9tn}>k=;xOTZAP7tplRdP)L+@n zRmVpL36BvgzIQo~U}mP6T`e?Dg(j)=pd1_cSa|c{umMq}pEiurja@eA%xAyOu5LDe~*%Mu#)%AP)x{}9e|`X zd*g{yx1H3$m3Fv`g24g*$?ZSOcn?6@^g-9QX_4D*4{NC4gf&syuJ~xNJ|y`8KQOXZ z^z3jG(R@F1C}Ha1xop54@WG^Bl{t^dQd20T+QD)6+f2Ss!@zlY>jpZo9|mW2@)7lZ zh#}(0nN1B_(>o%1J^$^0k=VFk2JH4TV7WS3a5Fm?8q$Lo&a(Tq% z3M~AL!I4jWpfh^LSSDb_ltJHMCG79j{-D!9Urnba-2~lOyALz&+oynM|Hg0OJq{p( zst7ltV+-#I#wK|2b2*|P`mY@0zciy5Iq3iY$}utyJdEObND@*SChkmWG#q}D3}Hb= zMdAYg{O-4x4IUnV5O5015al-zX}O}_+*~ogh21%w!6LnB-wsYM4+3dob}0|a!m17O z1cgGDgLPrGiBl!(&C7?StTeQ7-o?ZSBUyI({gfIr*`XuL)@d`R66Mc#aL*n;pSOok z%pI3cgDka&*0+@mXNMjr&on;8lV)QihiUiQ>YL>*fo!U2q$H$kP|T0F_sFmBfSg`_ zC2u+}EMvy4iD?0*Q^AAoY*6B*Z4Sl_ClC#1`8B76{q&nI$NezV$SJM4T zZ6%}DHB`4O?+BqFgQ?#+DfkX_L(b08_XM$jjq6+L)bYBxGj&0N_xo)|PEjhXZP2?= zYXRS*D#sIQm5OiLUvVgT-B(}I;PBcM7yvcBKvz&;lsg^slvbRuNC5Tx>95$rguzz& z{RD$%+Ypn543QG`{dE;3YfHs*Sr&C*E)C-SoUV=kg6Yes|XuWC(J@h+Ceq9*n$BvK`=n{=FPj+4QWIA49+UU z>uzrsm*#1XYSg$^H+@4(<=R zl~6}Y##`oP+{V5l1Rh3)#8D7%B1m*8j(kX{`77JT@~K%518S-2Y=||^)oGqx7Mp4U zN6YjjLrh}|Q2Y&A=jIe4bqkJ#q(07pO$1dKgA`^MxDE+<0?58|H=e~Oi1H>B$af8!Th;e(J}Goi`#|$!@pB?wA)Bn1uoCz3ZVz{DXAJKF z>jA!#Vvt~tiD>CngS`D|>`9vMlW_Wv-}v8Ybb@S&0kcYgU&LWb>sf5`8_zQt<}yMni2X&`aX`+GqB1%e}OtH&?^r zS2?Kx9sLO*Fej>~Eob6S@u3a7yqP_PV>uEJu9=mjZwbqEcH7sn4O8q_+ln3TU3q#Q z_AVJs>5-b=^%<;R*uPDTA;_O!%3`;DTjDD?x3#j$4sY)8$&-^t&y!C2#Ga4!w+IUp^Ccst9(#!i^?1H4@Y z;FD$~__6NS0C{q}dAGv$YQT^;r3ZOfw&ni%;JzfsE6f9XPv@N~@{flgt34g zgL2d$y}!$3)ZmtqJ|ysQs?04E57r&HEn5;x*iH( z43HWBivQG$k6AvfX5lvQ2OK}ek7nJATGu}3NdpvRd65sAO+9||kIO4-)n4gVI?PYy zr_bC^*ysc7(`*X5ZtQ2=ovckN(Nn;*A6|BRnA#^u?01x2?V6Y#kV1u9Yej{%MBZ+J zoqA~B^>ZsRiw}u#iI`8w^)3ilHRfNtzT8H|D`=Sgf6dxdbL4OFL08nuOU&6DrD`D6 zj`n_8NmMdE!t9P%xwU%To+5S;b9I$%1E_Urn1xv?Qs)~xZxN*Nvh}S``1}3MhOVgK~m<%)pb-ae2WI>)|u}WTxNKXC44Tsb3NifZ}MllDaP!1=_(T{V)QS*cv`+C8WCkHX_u*2B3+j8! zqr%Xg|KjHQCE~GkR|9y*q00hb2kXGS($QrD*Xajn&kao3Va@YHb!G<^?U80fv0VA( zPD15g4B9ICf>A69UCG=0SBqz=iXliv=xmz3MIr}Z6~p4OV=LSDX5mWU@_*T2(vbMk zWGV`o%J0-wK}F{cOVmK z7XYeWcggo@+ktF~<`c=3K!Uj#Q{ri&GS}OS27)|wWTaMS*OM@i`eo-P=re}kbGpFu z!r{o;;iqPXG{DQQ2&>m<^&~YR_~M#%hL8SByHd604XcSRdCBS&{N3Be)Ru??-gd&* zcCw3u)_023$hb>GrxzO94N2PD$lA=wiXa|TLCY^yeQ}N#T?Su7beR@;6a^EbI;z{F zz+da?fOJMi7lMGLv)VOp*@WTm#89P%iq?EfrcP~@N9Q^(>Vh_;>fe_3#OfF;w54MU z-82XLMw@ldR&xt8CrWjE9Vk)##T=hfA95Hi(X}!&jX$YhfP!RuWnJ+tN~N1k4j%Pt zaJZ^r<6HNwB_yhT_E}>N(J&1kF<#)oW!sSW}e-3Hgz17f48O`;k{W)_^-L;LKV;?>6@RUr-KvDZ7c*Zg&f_d7`ZVi6#Am}wV_NMK?$VrcN8?>4{FuLeN zB;yImG>$&Soe^Rl_|yW91Y$_3$zC#vJZBvVWX3Ujxv`7v4Z4mj$tXe^B_ORvW0dD@ z=3fby$HA4LwxaWo#U-(DD&5$Yh4a`WbHYM0n`1qu=L}JmUepyRDFgUTiHqM?SpDd_ z7q%Kb$C`N^7>e@mqgdqEO;QL&%lq-`$UG&f2GxiKS@1?r+;%WN)mNA<<_n98MxDcp zz_kN3ov$X6J}BMBX^)Hf8cKAWC%Xew&CC?$ziwD>HrMe}f=qn9-Gnbd5qL8AvGt?A zIjAcOAT7LVD9xzngTnB!^OyCOJF!boT^6eFdSKcP5}skjAx86pgfwfdQv{kX&60~e5$gTU8hmpFq+LD2E609e zYQ*vZOh1sd;kh-jIN-JAC}QzHaJ2{IZthnX-^jXRh}#FvWM723BMCC@Jc&MlwMC=X zck5|iw7Mg6((YGQzG$^Y=D%+iyis)Z@P=KuwNr?M^d5 z7u5lEcB!Bs7i*wkL8Jq%%-g3K0nV)@os-e;ooD?h{^|j`1bZX?SHxiX-y_EVTO3`c z+5dMqx>8!?s{e5{*8d2G3@j}F-)Q)MrXT-Rld{_o{U44l0*?ZJ``QaF^y4h6Y67AC z{wf3%F~=;S4PyLgaPj`(&WqOccp^2YjAqU=OXI_)ovDXo+U?;C6Yj`P?M&`eh#axm zvSgC>4yi1us;6nYY|X@>&ihEiE4%U*wbFEP;0&+!j|g=p$BnT5o`k96aW+Zpi{RNr zUihBnuVKQeq-KsEMrCR3MrdlQ4O$KKC!^W6*M9HjACu>fD%7V~=N`+b0^93`6(*AT z7F$!v(t#PJOFOf6*(%!+sx?)sB#3FpT)XC$ND8WmD^HCVChft6A?c1ksx!izn6yp_ zsVcpv=UCLCCOHP_j%kG1@3}@ASpgK@R#~DS~O)Yk*OhH$~&Cz?Fd4aw|T-(#wB{ zE}HK?mKQ{O+s~@54CIWIs;d%9lY{scfWT&pt9WhBYa$QrOa;Rq|1QxT&tTZZEL$YS zVfIIp?F+bVE%DqJk%a=G$_*A;08j8tF2^bs!cc}T&Ca)3t2vlGVrWy&AHD7}E0-nV zYF1OG7s1F)xH=)D1#-hf-G6Qu^=%hLI~oUIJphg5nGKf?St{97z~=RG_r9Ve3~AR< zc#R53mY@xZh|U-^-9iTk17|!xdRMv-QyWa1uRa6zO(({lhYnX+VyWMrNDnXM4uhkG z4o!y`EZN-XTHkK-fp=IwrCdW^!!i$9Hv0((0CPCMzg==DMC&qY(M6!M>@Ao>(%^C# z!7zj;G`nC}K&=<<;s`^9eG~x9`P5lpx z>zbE3`;Q{hH5(!t2NVHGLRpaRy_;wWkK*m%ssyt73#Rw1hBa6itHx6i;%X}sxNhFy zlTnYFY@wceuG`pIc)w9zem`?(^}=D%8Uo1H)aZ3{#BF>dR>lSkOxPu|TGNW`t6#sKaw^-}us{`>366}H3KnNis!)0WP-R7Wr(&F@03DhO4G?E=wXtXC zO9qTM&M1k2xTYR$pF39`E5|saMZz`|L_Y5V8ALY?8eY z?QzaQSxu^rEv+4Wg9bXhic3zwc!t|;0$ARY=?H912%JO2hz1Mcj5cV-1D`*==OFan z!jFtg8KI|rGxwLANJG1jcU=tCtCP&uWlL7~lVD%12dIzd8TFYjHqDVCUD7me^E9+_ zr&i8Uk1I(mL1@%m7A&SpJ{IDDS8|A1qBpg-BmM})6j_hJjX(ep``BL8wfz+vF z%nVayWd`8LO!Z0rgtH%OG7})r?O<{Rr`>i+7>6KH6tngKvi4^PpKiSy7U@z*awp1| zL^klZSM*~7n&0C%RthJ|cy1Fa8fSmtLlk2#^=Ai~o_hzdp9I;3fXDOn!(2l;(!dXS zHgW6D4&>N$W~`VdA~__gNFqPU0OHLSt^^82Pj2z4kPFN3>=TQWHKwf zA#f}33gM`h1Amb72e{gWa>{=p4C{Y@C;RVi$p3o&&j>T6`M=Wb?hmzF@u^^X)SXkB zh^km72TZZp4U83VjY8xp@j@xZvBw=Z0sj_pg-D#e_$Wxg2_NW7!yO=InI2;1w}Zo} zrLi+~W1wVpOM>~KiK3^T#_pQbY*pUL z4$}({JD(2=Z-+LnSL=jd+D9hOyg#glq*G}Eze65)U8ecn>{)$`OcNx8VCW%i{bfSa zY$Te$BO+0&geH_J&fMYrR*P@s;(UTNBOEiZ1|gbJeD?7CKG-4Xgp4QQQ8! zeJqXjlKIPI@&gScjHwqE695$%v~ae!myBV9?~o+hTP5DaoKhNSBT%ulq>UXOHk1i7 ziT?>rG~JI!`g0--h2_jlFvi6TNEP14mdsy5+V<_M0{;e2jaj?VW}UVo)59b|O0JOn zRqdlAP5NhU-Sw(d{iMpUJN?SgX`H^=2HQGXIlJ!M;AG*+@XG{ElU_Fc8{;(Kl^-W@ zjObwC%Jgrnwx1(yhMr+s;~WbcBM$k>BhWfYkAbhF0?TO>>%%f}?RRDZ?hRjTf>MbgEn$ov)vTwUpi=@%rEM%RWdx*9e>*U z?=Z2ikB%=LOIT7@2e{s|C129cVQ3eINt6n?L-KzAOIVUq6aMnPP`LBv;;GHqSc{Z= zWzWM?rTO_f=lzI%UAhTDLEl6=X=aOmb!Cdw^9tfe@<2T3^3DalCPh}A*pT>@eum41 z(@K->ohR-B6Tqa?pZoQtrRx)v9m;N$EX%h%bmx#}>|P-+#dgY8BR-BIMi=t7eG!0| z)kyQyHvY5fM^C*sI_tM#VNI5x2iWqw zwsqbewcs4vW{yXU=r74%Y=h=+9#Wj#NIDTqUFJJL-)ryexIMb^=5@CQfDY+mDpQOD zq3Sq_R&S+T1`KXtN`5!6^{GDrAWxp2PuxF`%R-y!%q{ zK0tknh+!W(XolgNAS0z~{zN1!f`6gL;cy^pV!G}s;P#$Odo&toiqj^-s~EZqD?oWX z5z$+!Hi6D_VL47EEDVznjzR&BwlF%2$zRP;?zd`4_i$nj+3#T3j$h}Be;xhWk}x*# zzyd)JM3Io>0B^E1uS|V%2>Exa(Cmos_hJpoviWtPwgF@S`AUQSLddESme z`;m|_VWQz&!NPd)PW{w0ELM|+>U8g2VBuLQ^oqcA_Q`do7zOP-Kfl>QobZ~b_ubpq z+3V%!dI3dvNdN7j*}KFEetp!6-(Nmb82~0ZQ*~}&D&=He8xz#XNpVbkPj43?-0n8Fz0Ue zD)9C#wo)?M%Qa~F&N;ZI??){NEKnfnE4Gwb5*=!8TT(`ss zpi9Vts1IVHaKKF@A@DZ>LJ>jO;921N6`Fjt>{jq_Cp7sK96onB&L7m!jbQcbbx9cl$8`4M?AXIu4zu3~H|>{TjYY?gJVv+hpwAP3yJ*1Zf7p82 z|5I`IUli~E>DH^c@f+=c;Paw>n~(|l3z+e-q8lGfDsnyaWJ)>|tiwpdjc}Hvvia`* z#mhty9j!ugFOcuI{{f_X#tCF;lQh4-KRi1cJJUH0{x3Pw*iqWx?cjgOk;aaw-s1J9 zDkUnOT$5&)2u0q{({8hpmcK+HIdXKXOGBY7_k(rnPFZU zk;K-vo$dW9l7#x0Lz)cnC#mOaRF)`&Sj(QGCTVC)bSJcyXig2`c+-c?ppjtLlVxwm z)GrEiNd%L{3}S`JdOpp@W2?O2LB_&}B@aSVTj&k&7VO|JuApnA`B;hIaW-K6weSio ze!rt^L^jMal~Gu&H3W4lk&avt6$lPw`keSyw7;d~s>qERhgdLIc)2GlNXjM>J7QUI zS~2BCkXnnqnvJLK+XxkYd%i&fD>MJ@z^%%_KkvZ^JZ=<(Of`Q};&215O27rUU&h`Y zqzftOR#Q}mYn5RN76N8=+VLzJ5hK-P z!jV3Mgl1KD-y)eSSZ2fYvryI{*$mX9yf+tZ6;pHS@sg?*=F~tt8XIT|RnrHMKMT&? z{^!z{?p|W~?sp_Ine=+yyk64n=k+>QO+8^%xNG^QxE?+!LBB}}>H$q@M~=E%DyKq0 z5*K!c7}aC^Kc~m48#5O?9aRq(g?HZvj{~fGqqSr^0%C37{s(OYzhpczjD*SW8Vo(b zZB?}|lmTjuypSXY$N_4&kI;>0gwQ-NP6luMS1%ZTNXL;|-cv73U#!*9TkzTuOkbqM z*u9Uk-y5vi(OaL{gWqS&f1jBRWBxqw!N|2_CK%&(? z1$M%xbUAMiI*gLGme*SfPuS=hioI%8-Y+4GfDzRQt>|wYS?00UUOCN8X*b*Gxw*1S zq;8Ns`#cxK-y~^%s%(KU{Gdi=H*pDRCipyLb;7~A_$spitOlR27-4EyC)t(1Z%mFm zXV-cLoZMJwj&G1~TV znl6}md2t8D|Fi$c+Ii9piQC~8hWc~euJ&^c!l0uVI$TSt5dQbUZ@tr+D=>7l8S*!o zp8)prG&Cu~Ho%uC9yUlD;jOnc+96!byZFRQsJDzez?lzu5*oix@zr3b!O{u)5nM&@ zDr{lY1@I|=D*Zk7Hc?BkJv@|g7j7S=eIU8>TS<`UiPjh2+j+SA0%!>#vVhx5^xy#H z9RFV-C&(?9@p{{D(!T5;bIsx5ayZ#;ClQZ51puezxWQ6($Y#y5VF5b_L%*9quDGOO zQPbI6e_!-@U-}`yAV;I*t7<)>2v{UckoqmyegQ0IQNs1D98%xK#Eq82G=K!9rMk-< z0JPjt75%CQTd(uZwdxH8y74V1x8NWS_E-SW=X=2xaARNSsG*!4=8!X^K^KV^>lSeM z#HuoN&UCy}wE=?3AKE-VOc{lL z_)fVuij_J+n*!xGC>1I1bCIeD@e0Oi40h9smFsei>pAAdw)&0TtsFivJvemo`aJ*( z)}L%<1hOE9$fG?O!FIv}Zu25vWh|QXStYOcHu9yVz4h1&i8wl9!m`Lx0 zUI%3txID|xC@|*GLpqytD?M~*-_0ZNmiS&< z^-Uvz42Scumy42xERmz<6!~yF)?d*JmpG*U)3*KpXS?}-DH1}~k3#D|ecFHU)p9WW zUk8%^!R#$WRoW?w;lJ9==AKB*f=fvwO)(1=rD4r?kBNr@FwJKGjsPcjHy!g()_>+7 zNv>Kt@}7tK$5QR@P5TDkESg^3NtuW^rI9Q~ErpdNkuvInt?H@9V81%4pN*Hq>!e^& ziQ2V&gwdzwX;7y_l2#9CWRV|RQKkHT{NYxN=Tb!V3md)hDs0Pa!uE&3)+a1=TSO09 zaWf`0Q%xmYmsX7`J8V9Ql>*n>qmqjbK;OS&9Yrg?M@KQ?0kMcUCOr5+(c|P1;AQ#PFX=Nf&h^_I1jL5 ziveo6C_yPY3abLm=#92~ zLP13^VDuo!iZsDIZ~=CQ8#dxwHI~}&vvqs;RcgKTKtK1GG6CQ`$Zxi^NTMGZtJ>&3 zA+LtFrT`FJyNK*a;Urw$H9`C)vH9_9!BUrChY~)vKT$&SBcm9#yeb1j+E>qU=9pRq zq}~8dv2ea&TbRPSK|eAq(arEGF6_}?hF50p5Nc)f1rXncr&U8%R=7{bB+6JAv2)ceM!iMm`0LexRZM!0+7!`j_S(WSh1zmI+kLW&^{(9ggF ziwQcNfuX@Yu^I8Oyp-(U%Q3jgb&x?Dq<%C!j@hv6-!Zu}-1uDj>S(?t0a!fP$dOJ4 z$-FM*R-1c_>-pds;A@*ZaCOULD@tA3A(aWkNWd8&_@O@BaRiL=>p@U{7lAlffA?wf z^QZB6on8=$*Bg>d2ShOf#y@flzJ7pRYTN&5J>mEdzJ&j^@zuir&iG0$fkNj$tS21* zK@Q2t{6Du`6r)erZiwCH)(}8}XaJ%O_}mt3;}BEgpime?z?D&;g<$Zh82ykto>#wMP&s6+bg(CK%1&__v-I;8$%(c%+ni)2zA)HEtIMg1bJ zW;?2Gv1MEFpeoAz>iQHQA)AiUA(BLzS`f`X!Bq54Uf1~&0%`GCQ!B1GeZKzJQdQHD zS`D8g>?%=%=Aei|Q@3%Q{j|-rgtLJ#50u8m>1m5>sNyi5~gf<&JK zmbOK&=|y8xGE$V+jr}}qVz<1^d(~kbDxe0^Vo-zvaABAi7Rjq#(gO1U1XdebVSrEy z7tJ8UAO_76cu>fQT%KU?z>kdft{_}kP-zAf5qD@fR`nqop)a~!R9VL!YUU1iiANUq z@6xHAU?(LW27l+H8;1rI;9fZ(J4@9m0&bP&QK77_#%XnhHg6rGv4wKBSdX9@@a)2{ zEZGTUW%1qf8j(RMA(vY0JY|;AF9mcPm&>A-#mXL^&|}O9tt*2#Qvmw2tHQl_o~0~| zY-%B4aVbr=m4=X%WXn}P?>H9X-LLDK!|WQFbO)IZj5r&pp)vQ)g7qfykI%2F>p-wP zWk!8L+$}*|Cm_B`F3%QyWQsz!$7O2m8JTY*q}j7MO?kj+|EdTk7BC@}EF}aAe{c1_ z91aW&H}?=7bNt)xTp3%$WeRGxEo=*5Eb_P}VdDSG=|YIRN13m@=U?jyy;V==B@GA% z3wEHZddk=iQX1?VMZqfkg+{e&MDmf_?S#e`*7h%P-_K&RY1NcadUipKnh6kay4q?d zET6FR8RXcFluvsl#|R0>3yCy~jocAkv{Rx%@OB|;Ky`ez$gFoS)HIYx+%V0qMp90> zCC^u)d%kY$Ua|X=@GXa%IqwM~iT3cTG(Kc@bzZ~DL+gfT+zRXY7y7Q9iG_(?f`O{K zqr$?g%wn?Cb0`DWMzkbyDJbz?a?mOk4KDH&kKXe5e-5x$}xj^)6u0J1A8q2 zu`mW}s&eNW?6k*^<*tMhmB(za1YEX~`%KAVGaIiMLvh-DHOj*~=-px`^$fn48AmCt zf=7MIxS)ot2K^W1S?}+mbCZC18NaA5I*;#;hS1--VES41)7b!}SiLxH!(GO$P&t!b z-=v8{ZmCX&ApwK*M{s%Q^V#t`?`1#I8y?4YtUbAlN}F78r0Q6kFA*=R>E%6()Ay4} zMrsu%!NV&KblJ5Bs@$kllw#A?&M=os4~M4nfm_*GyH@RTZ^Hgnz}eRRUDIqWd$OQ* zUItP9uq)6OtduBzK=FOcX!pia0&)!_O0a;Rfwc!zpAzU+Et36#qnjWiR7Ie}PyzDo z?-K@|#dNInKpXM3b>}Nj3T5@>Z8+44fz#IxfUpA~SosYsUx{-UZk0eEt+lPz%xF+F z%GW(p2Ynq^t>Bx~nrc_T|@XB#MbIRZuo zhW{D^IXXELaBwjGKkzw5HirMPtbIsBIu4s1=D&ytt9m98g%XGwAfvz=XyyR~V+B`6 z@L7YfXt0+?_yX@Y9}aBV?mTsWT&)Sf2NFXQDSYwqm8@us;-f(23Yw6xzAHicB^q#~ zOg~q8VSPTSR3AxI{t-GsCU(jTw$f#4Dnx6K7eK0+UYR&e^+o<1WI+vUjZ#8`Fs4Ih zWroUJ5`&JjjFuihm>7mu-5t&*(>M06(1+85V@ z5k1mi%cY$YMd2sap{o?wDi;dCL}3`F3ZN6i6;*c_v{Si`6fV}YGhtO@>?OJ6(Ik_! zGbAe1%hQ9xJ{^=QjOJSg$YSzRqYe+1f49BrX)+RB!d4Pbl`2z6ZJQUc&_M%B4S8Ya zMg5~M^Pi6CMOl@#oeY5N+PsZG=?6%R!p`ne>>>=%?3x2opVGi2knVrX1fnKj$&OjN zySNRb!xTF?CJ(7v+l0s$m-U~kHJ{q53Z%L-b~90^O5HtdFZ@Mue3WtQWvHrzG)*z=qxx z1ZFB$o}5)+G2q)9#-%JSmk0k#D*Q)7mJ4DRzVH;$aBxhFA(&+aL0ZB|23ri~6mxkV z1Qo_){?g1MFmwP!jWU9EdKzs)eXmfEC4}AZ?E~KrY(v~^C6R+!F+(#=wt0Epcg#l{q z*LvUl)34c}*4wAy?bg)=ov#1>sp6s5yXD;x{agKJtLLZkcJF+_vd$Y)RsEu4V>fi* zap->P+||kN!O&%6D8cr$WohK10zHd$A`P97SGS!B-yRArVhvIjiU*NqrWO z5tyut={N-bxy(rJ{QI_lH>2h$teE_$P4fu657~nC&A=>bMn-HZc{d8cqCo5Rg5~N= zm)g_{m};Sp+T;Y!lH~*`#d~t|hht$yiYmh`k5OGK!F3C-+d<|yba*jqRB02F6*=p% zOy#OK!w!9a(qr!8-&nHVSo%L5#AbNKFc&w~N|rW#{dNk@)r~)5o`aLC@+n-!rZSEH zwZfp07_@5KMaeJOB|g_&$iJ`$O?qYHacWMsb|pwYdg+Pcous^j9OnK91rW5$$V6)-(KDgoZAY7WSEPrA)6#BVHe&_V@ zY7;o7-D9a9A^SQCvd|(m|fY}j@Ml<#LU;IE;8NdU!`CyEk{14?wf`! zN<8A|Fm&ST23X3G;g>JeiA~^T9-jOuUIWFeZWxCeWC#4t`ZwWg$|z-J6L;ZnA=R31BR!%wX%{I*NgvD56v(3Fh@#B|YMNH* z^#$e=NOuY*00nn8|EtF0Oc6g;^r0&t_M1Qx0GF@g61;ZZLl$qM1TMfXF2t_(hvdLW zrIDj>>Bo^u@{#i|_BiC-8e#C%QqJ4u2$#S(^SqC2N;15(zlnb#aI+3yI(?^`w;3X> zGHkkT(1u(z&?~0-hJ)KRHdxd*5{z3Bq_Z4`IJ$$a2DDm_+Sn`~yYg)Zq#9)_MY3RH z9rBUB;6JYYZ2$X)#mV^}R13@u|97Y2|68?Cg*oPWz;=IKGn?Q!qm;Cpt348nd^Ba+ z&YL4l2A&y6TGCbqId6G#QOahYuqxN%(Iz2Kpddh77IS@G>!{AZ^ZDxjaNP&3p4%b2 zwW!U@S7X!^8Dww)ws-E(^S%|4_=F$V)9rQpmohan$!4EaAUK|{3VFw3ynFs4=H_Gi z8vOgG_QSp9E%>1KEpG1WSpVI$3Q0UI@a2w|-d9OGsT56`Z9)1ARteVac+3Ng17RkJ&?O(?p!8)i3%W@0LDP)Hzo5k3-ftEwOy7Nm5@9& z^}@zy$JmvxQg~)g^+9P7yi@<&S$qmu-zGOQc0^MX=Ui%qY05!eIjdSXY8?2~JSgJcg zwp>dsd-PH0Yr{xIFy!Y-$*EpTQG48|$S#=Nwo0iQ_TLu{_)_&h zAi5`0p1|YVK`5{T^=hu_F?~@Ba``H2vYOj$V)6WyyfaRGZ|B#kh=}>w@`?t4nq7GX zo>T#()UbZoV3a%W|G)vkAxD8M$j2E+z^-m}m|M#*KU?K?;pB^t|CKg>(~k2yFo}6D z|IxFQ>&2E((O48p!vq=31r5^9OkH^{W7BVGAO3qJRNBpp7%|KMy9g`n+(rH&tU-+K zJsz!R6pycjz;R-HJ*z8xKhcL@{abWZk6c3s&Dk!OXYMpyIx04w3@&{ zP%uCqO<@emkG$4m)G+)0QNPkKs$Tyv+R#^0jYmss(^4=87ZxYa;&@F zqKuna7(m*<(qWFRhbcMx>2Cf(;Ru1USj(&-M1BY5?S5z<=zF9TPs;W}Qg~%kkWH?f zjnn?hHl#HcLVPgmFe#p4Ms!ZZQhksJ#fd!dUjG$!(IU2Uq3=FVUC$xpCJefMZ`~E9 zBj35!eQYY>pv3No!5V2BQtIn&I^dAAeN81Q68hvIvYEXHk?1ho7C0VIf?G_sC%k>! zj40=VqeUj^H{Bt;Rt!Pus$#OrK-K`mX)$vHgbqtu=jBSS2q}O`rwv}c&&f$D;~t`37*8hnc&QbZA78z z=8bmxW~_AhDJ+x!fdOk^cgr2(6B#Agg4@sQoR^EW+X@| z*sCS2!wd=7KsK5PO~z`hV}X0TNr~!ujZef9E`$e8DJpm0;Aoz=T17V=E)R zJQg7Yb`%^b2m>yt;h-fFqxdwgE&`c$=TMY6jzqN)et=7tW8YHG4pRworjW$dOv*s% zy$@0Y(ATsBqi$xqLl^B^X^575hTd%yuLEu(_rFuQsC*k;g6&D}DyyjLU9$6_=aA*| z%wtA&r!+sHClfUY$Z6Y>S(&C_e)u=MC{Lv6Q(1L)diMB(-ng5VWRWJXV;_WW*EW9D zZ5xlox@%m1j=)%F#4{4496F0#xVHHEp|K z#rr>_f;qSdpsqs!IdXM!=LWSMAZUEBBBPt7cL>LNpegDuYE3)`aLEB!$PMMz zVq^9|Q-NTt`2%QDKH&iV<@pCPIDJy&?0OM@BV*T_CC_EUhwL>Y?kdRlF@P223azj# zVHY-%X`R=If@`T+NbC>(@Lp7nENhZ^RNhE2h=Yv!l0xrso(mVx#iP0v66Kv~?2FhCY2cZLTP;Qk-Zn8fWQ0J*62^L; zm4y5|eR~%@SZ@J_op0cac3At`PpN{=avNru zs3ywGHoAd;^>Fx8Ya-R^o5^7$W6fZ>AZ2D&)L+1yQuuT92_XFqDk{76%JPK0P<3-- zC-olZg07|2~AWe)Lq(y}reyw=j*CU%2i77%iYDb0rw@(CegxH+|#5m%0ambACBg;$KDK zv(u-g;DnEqRUo+Axx-#6_x`5gfHrTuUu`ty!)`ydx=64zwD=Do+L){i7ifm!H55=) z>>6KeKA?L3<%x`98fOgfGIb*``UyUW!~W?DtkQ)F4X!n3k`UXsOdBRpwQe<4!mXqk%`= zdk=`8_!FE5Ehs7)G*`qJfRT4O$Js@6H(fMTztLp|`r1CAeF3n)%(_JQ*gBB=zX&|( z^cAug#P+a8eiOUFx>UIXTh6(lR-qVXiHMD%TypZ>^sItEAJNa%+R@XLMUJL!)}D0! zsOV*QxluL8!Am(M;KAyvI<;}wj)*unL6-R@uYh>6St;4AUdGgMa0T>?5M;yv5Nt2u zH+(A%u(~aiNSjbbO>f_7vFL-^^zVPPICJr>jb~1>KE(?7oO4~)?!tk=U`TQdWC?ef z{t2!YEn?(S_Rm${;s*&}+;*Ta$yTw;+Q5&=k$>lcAk!(gHyoS92e=7^JB7zvHJ*W& zN=|O^ZXTfji;|-i%xy>oSHQ`3ao~U(i>58G@3nwg^-?!T49y2_RW1;eF_aATiyuu8 z#G|meR*Wtf&)A^>^o935x7s_nDH<%nK z(T5N}3CII_i$e;RRb#5~hw+%ax->4}j<2Cus?P&N>x_sIJrMl94S*0r&2?nZDf!s6 z5H+EzF~E)eE+Kx$H%-|y`R~QRZ-44POk|nam|6d)LaGp~3CDxsyXu-<(~5km1TeGP z$*Br=p`P6-FP?Y9 z>Do;>|+nA zP2LSV^E^tnkdr;Hkd4!&05Ip&Fzf6^+G_}sKY)5K*WsS`hh_5m3GQT+5(sn`M3WtGL=b$fcpqi3&H&d}G}m zV`uMqMUK&qsDtdT5gShXZ`jq?J2GY_uIb5mEJgOX%Uzsh?U4MKq} z$hLD+AN5xF!;%Qjj`0Xkge$h&we~@g!7_;{ z?uSrL@b7P50&R*e*VBRAiH%^LDrGUC4tRwbA&48O8^x&PBbbbLd|l8URD`mI${y6_ zWR!CtT@Ti|K_526XD^Fdl%JU4pvC=KN^Y1RRf|o)m}S+W(g#MDuVyiM2yEu=u}*(5 zk`@FEb~C4&T*0$+UNP+y+g*;eRiy_)k|^z*Q3;Vk{7wp+#a#Z4dJEXeHar;Fyf0hO z$u*-8VOmldtx{jg&OAXdnotn{oW@mI3o*6R$IN}6rSqfCNnXa9ARXhg*l2y$Y9;bq);*))O-Ef7q7XClJqg=vIadhS3r zvqq-o=CW#NDgovq$UJqgim;tHFL{RtgGs>m7bkt|!yMVvLEBS&bX;J&1SAM0PLi#h zYV#2pB4z&GdZgI4mKp6&D)#vIIylvB9nVQPxm!FaZ=2yAVXIDf8*xZY%j zg@E8@lXElHW98Tfl(BS*sMdDeG1;FdxzX{ zYnpL|F@Rw9(+9IQ%zZma0))arU_b~=LN4@wlh$0ry3AEcRal{X6H@=em$TzRS;<&H zQ#ma9x~r0<15%P$C5vWsz$`iU9w>3;4`?;UwZ6eUFZ!lI9A!P$>R_e{rn49YoH~^GO3dC? zHz$-6;}sko_NOJa$c1yxpd?F6FstjO6O-kdt0wf+%*Tm=-T$I2|EYwAF7Kch#a5hd z(ylD0Y!FgWj#WB%RwrZ_8%N1bJqi2s3{}dX28&N0b}N@^f)D9$al0At5##aaa+tIOyuo)xK ziOg%*764qHz}@8f2L$pMCwTrZz)N^YL-vA_A>1YYcnvr3)`hE|i$A!+Y;I%HNMc7! z3PWG2D0uZx^)ta@ELs*LGyUR1M1nOJD`{*!1Bm!i>Ry9qob|*k@f!j+MIP87-;&lX z{-X_6UJBa8&(OcIm)ey)(?Ih&QvKsmnrR((2;HZ2ce2W}aO=424Kj2h#g=1VqBLo0 zNy{9WDy&8_9vxwtIcP>Bp`S+yUlF&6`zvn#WAUVcSEFWcD-A#d;krLIARsF~L{N@C z(XkW>`y9;%*=6K|7p@npvK{8L-hY~Xm^P%r_q`#8$iq20#R>|o$^6qBu}FC^2DmSc zt{{iqVG*Z=vAcmT?!Pzm;1ah5U0Dea~lb zq_iVwNxo@iFuQ+7I6MwLc}@aP`a9=m#24e4ur>V!&Tu_sqs|>Y+;oL-T^S1P*b=GM z15Hu8^;>pk=s#8E;Cpn4Gma_QPJL_+F@Awf-Q`m3Wg*uf7I&7{UnsSR++>Fd8etmi z-A~~htW9#D!u9<-!ozaL_R~^f+ksTBt16!VYPfYV=om6789+VH*`W-C3snSstZREz zRY99X3t77zMn8cp4y*WbtI5QV9jEZ=oO+MiSA@fo5tvG&&VZn4NkvK`YRV)|q7;)N z)3AAo&w1QIh?ofCwI((m=f6?i*v;<L8FUQ z|Ic8sG-s+NQD*=%DoSqHoEVDBO~^4Zv{sxnT^P)=a>k~*2-+t9$5Ou6@7xNQ6ch#$ zae`&Xgdk^M8aE2N9juH&5IhR|nPUGvx)5q|TWRhW#K20@1KfplDkm;o`)O+GxD=szj08M2;q)&0 zkLvZpwJ3uO{F*zDvarSr&EpOV@~P-X4;`tgtGd9gL1 z9&&M=u%fz$d0wMY>fWEhM8P`lXX01&ui9h|fcY=1YG8?nirS_Rso1PUhW! zr?l9J9-}`ekd1>-8jk7YvyJGGaH%-ez_nTW3O=@`2dO{!;djJICFY+*o<;KLJ_C*8v zb;Y|9W7q(Xc&@5jna{NiNqO#h4IBP=Ob}ej`2>!fsaVuLepodjTu4cn{Zf~_04@cs zFuZSj3hEn@BFzfMQC~4|!rDB-+@H<8p)Rc|9YER>6*G8Whz)Eiy256j3!1nq9VwfM5QyecKzNz*R zZ-D@b_0<9%J$u?Sd}(?OR&m4Kg-JWWB=tI+sSno%$Zo1XRzJ98cm>5u;O{(5 zfSlE*gqqYxlO_mKkWN};Y?}^j`f=cUzFyF)q9J;{ls{WorwO_edB7f}!b!a;YY)J+ zg$2-~FF>@8`u!>;LgyiE9MOficvdHjn~`=fACE8zUQ+(CHClcf;WDKa1R#R3sN)w} zq>r*NBw|?%b#Y#=nD972n89XfvNS#!02UY4(gNPC(*hZ1Rc>b!_^mLww)4GwDSPN* z&rv`z*oF?|1?YaCg{S7Nl=w{kihwr(xV-Ew%#PD->8E#V9=Ll4Hz(M5ZeHX7;_?i) z&)<5fByRA%22$K$ZafY_{Q0vDb~cY>P|SSsYO681v=Bfy6S&O;C^SbG0PhOolDgob zTiai*1H!g`ZHS9q076*KUFag}Oak~Gw+SiU5-9F|Fk)!3Z*i2x9A5H_T5LVGq~1rU zuw6zZeMLrIt}ON#RK=&+c)CH>zBF^po#nW0b{lZNA=j-qgxxk=>d|H#DM-cjG4O%J zW)G~oNTl)WGUUh$?X?KA_#_$HA)=&GaEgzC*-immrdIl5dB8 zz@Od6@osH7efR@&#Dv8DsM7eYUyt@r{M-NBcmGon%J`p(P)3&jIg%3ETQU>$F^_d=trUB%J42c3j(+2ayow7@XfPbAyoTdZ5%Z1BDghFGh zwn$WuPUnyF-4?hZ8WB_y1!9zZ3p6>p+y%qD*_}nhE35t7{fj)m-;3ZHU$V0UMifT8 zd;ZLX2I$dq?L;bdv5Nf@FE78Iu6{M&fd=?bWrO9D^s@`)0l@~-<`aD5n`S7|nU2Wh zT6&eLv_o;>Y)0^@?SwNhgfmhRD4Yz+qO7KAV=B6fGVZdh9vW*E%Zr7uK^T`+TK%&@ zyV&-3{?nHuXc7~>ebpEhkkrLDCB|kEYo0uN7FC5u&-nE{mR#2WFYcm8-7&CBBt*~g z5H7BjR5G@S9waK(RA2&!77!6ic)p>hb_zEZBY5JQ>tGAae7P?DSzC?3$uP(n8?k0Q zc~u;V24(OAqBs=?jVM52I03d|N2d|f)!D>_m04A#tA#SNx1{?#Ex~%~=*~b<_66%1 zjCF6GqI+nSu;ltGKy0I=#k^%RTpBg3k&d2ELCkyYj@?7083`!w!IJDYf&iSkE5!oc z#-YVP36%AMzesQ&JpZ?Qam~*+JD&?np}j%D)kCQ~x}P2dh8T36M4|#$Q>|F&G+ETs z_5RAv-yyJPfky_(%l$s#Yc#CI{Fu-?*I$$YyrZZonq<0$#oVE`4i{94f!$}Yj3pu0 zO#dDHO^rQ45IBpxj^|!`eQWPv`x5JsAWSxJnCYCtz8nQwM3Ceig|DM(?y<3O?$DCR#{zt}u z(J*-wE{s80LdHA^E}~~1)E<&c4yjfak*s9FC3YAkfZAgL@Zy~oiR(bL9v#EF)BK`1 zLGWw}+W&>bNjGRMIv+%!fXzoDLd5<-0^o%b98^QvA|gMEtcN=poJ_lDsz69t5y$>{W70BSUxSG`<%Tde~MBc!ebd@_PMWD_S5GFuny^F*F=RuMS?m zd9t20v&Qdu_7@f+;-J2fVgNWk>Nn)6i-DLE_>tUv_`kk?cIw)m-}RW^>u+%YJyq^` z^~Tnd0GrUuKzV%fn9};?BGS-dDgF07aNK2|WtmBmJQBS-sjHJ=s#Jn6kVr=S^9s6C zu7DPxl!0mhe@Z~M16KBJS!F0e0x&B}2t!m%DMFf}^fovOqd_-t%B=0*CQf&cSZQ&D z>rN`j`qw;JQ3JEGmG6nk?%Ys-&!D^v6~Q%q-F6tOV;$06`_5iWy4_1Iy&LqIMYvGv zow_#MrIvtnTZ1yhKsJHL;N&lkiACs8?q+B7QQBh`4XuwMx5P#j#GBFEQ-Ur)t!@R8 zuW6saS_@*TwF2+jog7Bk?ZgV#EpW& zJ~l^Svz*^)@h$gr_fv+bY|><*MJm3`yRpGO?wVH`dIUG`?jRvv>w(eV3CB(fL5yNz zG&`)hfu+Rcjmv!sLyID2s#@Hw#%{B3?9VJ1n^!Ne{ z=QlF!D)BsGut_lxcNqK)VA6_o?3=h<0cxkyCctl$2Sq6p5aXYjOSEb#oWnL0ZUC~B zWno_k^rBW*8*@O}qZqLC&f011I6NHxHgHxpDBA7cya^ma0=v<7j|yk$5nQpU59m! z2;@Ws#lNq?3`o)Y<10ZlCPyNqv~b!oDgtKerkMZs?-SV*Dc4CF6&z6jx)5#)|KNS4 zz}9?`Cz*v{uWf{f9*vD{bdH+}dEFblq9uGZE`H_^HwG72EBB$Jkt2Q(A1;DF*z!7V z3F7b7U6>0(pNqyg5%+xb2gL=Ux^497(bOvV4^Vw!DysKXDNr`0H7}v1-*B&od;kMw@q=g+q zr)(7dXGc`ANdgoQYGeTcAabT4zYa2Rbne$S zf71UIvk@K|?C4LB;FVf}O52pDstI}VPA&qoLM8}kAE6P|8OTCD^_*dReMn(RF*RAPT6S0^fBh?=3bLz%=IU<3Cr9e0$YAqN-8 zI^FRAS)qoI;YsQ&-BYSw0T3xZD8w-C!$buBQQ*OPzv7eNG9ytE;MQq2=^ zuQZ5oi=&?O+5plTak}(8vtb^6?rPVd_*dxPRy?F#k!7Gg468E_{FaDt6>lN5$9eFd zNr9$^UoS{QE+eoZ?cDPr^xzebFCq!CfNqwGGj8Isjr)X&c{aom&?pvjKF2j<+3`>Q z`p_fa(gLFoT)B1I@2hEFeqT+4Zg2iHoK7c$0S(<+sIX2~;ul~96*WNrR`ON53%a+p zI@_Mn@0FM5)XW8}Ta4V=inw*=PM$O?AkPS{T4i(Eh^JK5ni7EBtN>QA+A|uEh$8{gXN>W$vG>h8MFzHlBY zrJrECIQ#<=i)(k1d&dIICB=N8$2y)6ojKCar@5}DoaL=_TV-5f(lhP8^efT=E})oy2(5r_Ld#(R^sihO@i`+<=o>rH^}*X+h^W;){jswp(7m%PB(LH@n!^HX_3fyYACbz5xEGkr(5gllRN3 zAiEEk-G@5y9(j^4$bT52xBvAvVfw%OyJbvm&7CcXnAq6=*G>a9|6}sQf%0SWbHb*g z8oc{Hr0SLK&aR#7csV`1JxM$!*gz@H-FlMnZ+NV3XXVFj0Bxk@Pj@&G@YA`iGe3*K z?c7v&+?U7Q(b>O8ZBy~Urv4x--6?Uo*zD!#-^`tvF??v(qk8B|mqWUoxs)Z5_Y2NG zZoVFD9`ZK2ALhEX0L;Uch6hoPtWn*sY_1(?z-V5rug9lax-O9oUJ*c^LvA)7HJ6p! z`Ezt>ERW&=g)kz)IaabNpP9;9e8r7|kneU5E~hV=LwO*sccj3Lw1Or{uh56PljoII zb6Z8ieyTa}RfySKROx(60}7a;zp=^s#hoPb&a7cOx$5DcKBC*=w+9dLyZT%#FK@?E z{A31vkEc<)kyzM}l~i3it1K<&@q2lswwVwXgsP$cjaPjkC`K-Z6_IgD>SRl4a1@Uv2Xasz;<;hMl@L8+lajzMl= zW5l|!Y*0T6D;51l{T&f2d{E^qMr}+bg7N}GeLleEDS9ca!V*h^P~)Z-{`b%BZT}2 zwbuAVJF{3pk_v{x1Tmt3D2G?SZERR|YSP#N`cRlvqeK+wtFrJ$mzm*2ldd z1&s@fHKg>Kzdwv{1DcL`KR~>3(Mg)pxZ%^AQJjjVwXou>yY1&7HRv%sQ47@8x^Nt@ zR~oPTqE0;CigSR=#!adHEYcXMTEm z+~7R&ytwU6b^fQ4h9VDB#Bmw3s^#SrJ#)H{>ioz#L$unAzeFtAi(9*_tNL+AfN5VhM?n+RnKgYr% zNPpZ(f>}mHS; zSqv|jft-2&wgH(f3Ck5jU=0ovJO3$6cL9b4E?6cs&8(N7Wzj^fQ3N#HwnVy{#W^suPL!Y37u(1FvrBAG6L*D&uj= z%#Ta}YoGuc=NqddZvsuXQ8I05YEI5_}MjguX%@g9nJOXN@Cd_%%sD0oe1 zKnmq*jFar@QuS})fB-GTAQ)c*xmR5lj`@DP9w#0t)0!%S^xt zlr}*2`g9Y~6Mf$4GnA#rJ!wkJ*EB{rFUN3PXu4j!SQfAqv#=$9GGK#-%Ch0_mzWuq zp$toIS!Wxfu+g8i*io6y1%I2OAH@g^tDnE9Y@OROBG=0LSn*k$xJg8~;Z+ymxr3}X zUR~J(6Xx)jjkpu5bKJ@PGzlpl~RgKwcry``0k3QNolHtMasQrgk=P_OANbL9iAp4Zg{HB)c-!Vf*vvEyyP$-1N~Jgw`m zUcDk#klpui$X)8Jy}BZ_&`*2NEB!6mq%tU~U%^4^WKL&Ql+-ZKV5;q)aQ)C<&*It8 zUBK4Cs+Nk+HoH^TT4{J7$)=KH%6UdqjqLGn`YE~oEWgkSpWL|TmFz?}#rZ03dc-H& z2B*0xE&uqGrHC(vw>~XCl$rLDF$;Qq+dV-CG5zmC+_TCZz42~*StT7!9Ql&XO7Vu> zFfl~PWK}mfF@aJoC+3p3oaK?+VOiYfjd+CqT!)9qz=+!*Y2+m01~6fgj*(!=xE>ci zXz0~F+uT{xN(WUjf0oL`#`SARS+TTTsW9l-xQFmhoHTOcm>KAf;mP4$N{S0&xhxYO zxGYa<=`FRyF=ym)KvDQ+M)Bk&QRS)FIvRe8brL3aMN2%$7565`JQJAw`}Ur7nZw`t zf9&f<{tY#tJvMkHAxs5CIh)XN>aFTJIJ;@PQSo9b5;)X6e<_axq1`QHO5~QBJAU{ zFts#7X~T+GYE{kS8R>Io8sTSkJ=noC^=#*bDahgiIElk~Tb13*vaFlO)e2tCpWB4D zjX-BXl3}bSbPQcNzgPR)=6XJWHTxP0*ri=>@OOc7WuKXYJ0}fveaU?Dchzj_|EGri zkNi3l8{_|4!(M9r;QKgGer6DFBveD7ZXvw0T-dbNGKb_Yrtp|JXHiUli=*(8q>O&N zor(p~M)=e%dgLyc1l4?V-(MgK@!Q6-HEAY`CXQCqB!|9>o@gIZ%5DAwxMpxcg=+E zlhdmMKu4>#X!BBj@l~$Aep;1)m0RW4Z}mx&>FTHED|oNZ`)H@!FVFWp%~F%xS}Ph4 zE$a$z+|C`zTbz}ivC7Lg*vINtm!^%GELC*TtkJ>($z|0Bi{pYqJsZMh)2HTN$5oxU zx7yP#sw}f0&003*3d5Fs)6H^GkG*84hqddw1crXtX);yq5waZ>uKCFdBqO(U zHq;+nnLBeI2*=`ky07(>S&o?SEL`-qG!reh@XcA4xpWft-u1Z|+p%yghtxg`vv2oS z_sw?WTxZLRi*WDIont6+aTk*KBSsCs&MAH4@Ex>gfiv$P0{F5<9i_~PS)BefkAG-F z)KpQ`beMOv)tIRH+Kyiq!r{s=k#jKb))gt&D(rPy>%|{0Ic+!k{l#qD`|0y1s0AxzVn@p8@&0lN2R0S4-DJXjto^m9Eb$46oiTaM~|}d^8*s%27-Q5 z4{gOWKyF|H<`gKkOt6&c3@(70zL^_T#OC!U_00(&b8&BV4U^PorarDs30uADGUd zXc;<1%euZ|AV#~rNEFR~kUQtdaLjL8_tPQ7YZ5ireL5H^qBU?pll*>a=^fFd(Vwp$ zH))$ZFYq5gagZ6`w$;Va>v6&zA;if767-^jQy>!|wlv*K`ZiHR}+$($V1+_)f^axo`Sz%dGve=WUrZW$EHs4O*9`>q-f zzX2-y1)G9g67JHL<@KYBbn5+FBbT|y_7?4T7Dzi@J*bxr-z1Y=iQGB<4O$j=-gfi4 z#dPyZaqLwyG~=qhUFiB>NgMlnG5zWoFF9P}*SWn9p)(GC74aOUQA*ziuCH#cQvPhc zoD~xqo06`6jk~JYV{Btf2rm3{)X5V3_)PPul0DDMj#anJ30we?geer5t>y5Rv8K2g zcfK*}+_cZeQo~NxhW443T9@oxeX4$pCUH)CFQ`tphfm#C(qk5}A?L9g!TX4dJ7NopD zBzh-)Ha9Yz0Pmb#HKd9Fe4WXfNwBuTwXu|VU`StZhpq$l1n~7#@XA&B#pQUH2IKy^ zdY_MK-jO;WmyQILim&()0nMKSp`NPVfPe7)5sSw$0v(}3^w+id==T%^hXZir z|09|ZV^+M^O|7rwb-RuRDV+atMz?dBhs7$JE@N&y*ba=`vC zujFEk1F|2?c+%o{v!qqIOB`-?d@)7i;;aV_&GEv*pth0wb~ZSka~^m$NaR)SXcn_? zFuR@D2feZq(ssVFKRe;=(UgewJ+RsyPNlXPY#{zc_o?CWl;kQW!rr5{kj?{aQ927` z#}(gO!BFgM85bKzXf}*B!`m~GV^O~w#=+23PaK%(O8)GS=-Y+3$uAV1{78&h%Oj(ArZ@2@ z#8ONWbMu@X@Lyj_i8RQv;uZT60s~HMQ&$rccg%A#()?@5@(yw*HIlXYg?hZ!jI}|q z75$lJT0nsPMR~(HU4E~YXiql(4-{P%J@dgFkD9nhi3pnf#qq? zY#it7PFwRl%fYd-R4hTvy^<|=HNNQ>8S6|}s?hYp5QHpSx5 zZPpDbBu3t>QCt<4xx=KDaB3}Ip<>(Ww?N}w^@Og3ug&r0vLO^LMPX62D|Ag1{e}^! zmi&eD$I~1gIjSvYo?6sV!r)J?M8lW|;oD{KeYrDhxrNR7BW}c1)lR}>CkqekB;?-$ zjGV9d5_=oJs9ULdEF{Cl+m)*{NZaDizX5#Q`!}XeUuo*{<32cV%gB`PR(0-jdKOAz zHtMe}BWx&t9|)!)H~`-)QT!K9S71XWZ3vOUP?V!VW&2>Erxm3(#o&nEXoK_vUUl>L zT;Suok8q03Qt}pkXO4&Lx0e;ed7~C-PNUdqtyY*oJ8aVo~vx%f0ly!^pm?i7Q!e_8#ii)hi**I-weiLX%;c8~(dU zE+T|bp#0@Sh}tQV%p;NA7uahU*th@Eq0Il4OXXzd{ND~6^!(pCR21%(q<@ob`&O;B zJ8U)gU;J%Aszp>+ae253-b`JduRj{q905hcvk|aGe%D+aBK)j_J?8L0pWL>7PB*Wz zB+)))j54y$7K9pv+BxMipj`_>e^8#eY_M}v_0_sI8HT_6U?&POt?XWYb$Rbvwl^p5 zc*kr$#N+f>_bVtO&xFi;Mg7~?3oxk=&&D=rHtASBC`r+k6r@U|Iz-n~+?3H`*aLo94AY0c;hL7RS#ejn&1c``VX@wA zwY>}2udlHDX6T(^^z+iKD-0XO zx~!8~OHy^Ox~Y&@1|#3ixxTuIeLo8?iXm6gm~CiU#FaM4e^D9X=wLl8dsIoGFh!b% zqqM7N>DCTjzPfWa7cNtMgwtPRV2uMSy0^5y)-D;A-z1O!u`}%?ihniz+Avb~EFpCG zFx9r}s%NUz29=xomWNnct{J%R2}4pnB@FJH-yK z(I5`q-GR?oZjo2*!TJf?Lmg<@IxE(4H1hr8+_V?}m!|1X)aznz<-UAy$YNtIlPskj zrcW;@cuWu&e^99!&oF>?*-ge`_K43bKgk{)_z{ojE83cFpkR{!L}?F~l%snWQIDq%R{RvTOHJnOwq1Bz*TTZhec(?x#oF z%lGVpSxT?&LQ8y~Ynqew`ZEW(2uSQY8@$GPYsmmH5UZI4zY~Rujp?FKqzm=kIcEGf zO+r5`4AZUdCuAp4=wu0~=F$^5e;+=4R2Q@?37$!Bf}B8@dMwncecL6+1#xgntH=<- zu2Im6bZD+j1?vqe*NM=lxs|ewU<`Ofc8^ogr`zG8t%3+_m;wiwMt*PK1MXwpgL!r= zOk!I3oqHhr^2XFH)JbwQ=RuJQl^_(Gwvn4!Zym$Ua4-;9SR8nB2w-nNSAhcr6fk4{ zr%|X8zUb#Lu;yp4X{lk!@LO_vs*j7(UK>aX=vqIl+O6cZz#xoJw(P5m#;zRweo5dM zyy=Eq7Q;WHh#1m$YFsy@Y-&8K7q%MHwY19|$aiJ*7>q_ZxnWd`3ul#us{vmE(qmft zltr{GVp=Fg1$uV%)=etJgF11hRW7$J2cQXks~FpVt>y9Px^JWjWw6GCQOT@9P|5L- zR3ZfZy%pawi$JJ-QcfC1`08LlQikDm*A~w&LPr;V`un6k!EpfZB0>n;a=!S$NYQk# ze`!X<`Bbet+x~hgUS%Q-@Ah{-SJG&TG+ZD%_t(|J%mm}dJBUq{wT@aQRuX4dsvzv5 zMSRi>FLl!s8XBy;fM=)uwLqG__l#-bY*@91pg_lgRZl=vVnv{XaWw)jd)yc*%2+y& ztuaEILq+BfKlno`G37h6&UQY1PoR1QA!plR!5%B$%4bs!l%ENy4VruaN^$2Vk6BNU z=&!#np#|P1fezg+n`n#4d9eH8v;5COp`2jm+6d?u0unj^I^+Vv2(R9dD~r3r+9D&@pt^&MWphcyrQo3|!fG)6 z;q~(D-#N5i#eo1=MKnr$r7<$LWss=L-60o3ssL!42(`-q?^HDiZ4zaJdwI&h;n*l6 zVp_NWC$8ps?mlpns)_TN=Iq4a%OJf74mS9t5;Esy!k25CUh|oThv8l>VPCun_BqrQ zi1C=HDt=Uo*ns4>k`aD*iY<-n22`ws{TQE8K2U@zP-7QG4749LIms`53HB0N3GZ0R z-9em;G^U79`r8xWMun-vRuQv2EK-kH$^e_lhX$v2d!g%fW;Fcd%O4e;MZ zcP2KE_mOVp6^*zNy6!QY&Z#}w7Si`;zVeo^wS4&ef>Y|_Z@C_5>PZlO;*Qb@gFtWy zf_xmJTb8RcQR!N)#nf#b6vhGzu8MM>=j3FLNWmRpY(l|MIWFT_VWhtq>kcq`#1A{R z?S3Fj$}%#=F}|&Ho2;Euf8MwL#9d;r@^3ce_jC^N=3Nd67+0{ZzsiRMMs0qfJpL{) z0GoU$FnGc-ro=KW6*deNCAHbL#BE?$fd%bR&JbE7BFQQLf?0iOyA66KD)A94{qTXq zEjyOkgH>2WmqcmCyraAO2`f+vY7pvDtR&fYOljJUL8#-p#m3A?&%t#!A34va2R z(2%5SiEn%iiFKQLnVh#Q7D#YcsFNLV!XZ~s!Y{m^gNCXH!)8gxD1g;@DKJ&VaA{d< zd7v?6*;>6}FN4AR1a19S2je^F2wjrK{s$g~z!PM-0uLEFHwiK#>M3uPj;KS87x0Dk2%lh2(#+-8-gv60k8%ShXcYpJ_pUHvqhQze}~4Jlh& zZYFf%H+59NQ}K=xs^*IDSdpq2h~=}go^&xuU4hW+GrY=B%uXhQvoJw$s~UOXJlNb+GnWMdri zcWZ)>YC$Xt-X65w82tCVG5v?0!2ZOIa-sZ=9ukB%%1@UACVQvaGtv;~CH3zPRadtZ zEC->15au~2IMU#*L3x~ha96uqPIzQcZuDN@g`g%VF9&>kgX^DZ<cfZMPD$&;y@0yt2Lkm8DOw_LZD#BOkfb39-ig#S59l^ z?_SX)rf*`<9o7DMSL@qPwv_LBsIp0A0_lJVSv^F?=H&~zS>rgYGsR*5JA1#I-Q&O& z0XUoCjT7a^8wJF0#tIezUvPYX68Od$LPqdjaCFSr0~n58{G>dJ0g^SJ z=#z#g$=L@(S@%F;WEQh~NWN&Df*btyKcDj!|~By5i&k|Gu| z9c&V_H19ZFJns&V_^;_xE~O-oGcXB~!3yM2>*gIhw9%7fs8aND7M@qC&Z90+Q(fM6 zDpyVV(DW(h%SM>Sb-zVo^2m}nrm^EvFSAmXNX;Ud%^h)`19iXdfLSCm=&0Z1RvtcG zNP;E!sb2T?eQ2~l_eg%NOi&~vof_%duy=J`7Dnr~A}-LTq#in}ofpkiZ*I{=T{tLj z(pHv^+UcY_cIsANH;qa{p#H){O{k@qFHu}riD)==?)YWz%FrZ3Y39u0*Gr|IG-;nz z8>!miQFvz*L${v#P=yVGwqu?oQ_PVVlpqmZ%r^2_xQ>y9Rh4^A)+U3aJ!-|wNRpF~ z?;K&=aUe)%j`2~Yq34yBq`;4q*s&>qrp3rM=J;!&%3@r%!*kOnD$>`G1CHGi_+9~= zec*DVZ1u)_d)%|Pf=D<8x@YMz*1EKeN`TH$SPd5 zoCggs!s^6lH>eVkfCW88b1|$AKK6?+Fc<>ek56u;iCT-$r^H!3Og+E&cF7ysmI0-d zq18FrZ2Szl>5V$ftOs@jq}u=UT7^IU&>JD)|uG+2F|y>$bAIAs_>Fy12cV#lSz_1$-1H?Fb3ELg%b}Yd`XijrBn} zOf7GGmAe@yb8~Hm-P|u5x;})0WQ_a*I70*B_KvPYJICj1FbTUEc$*>R{hxNc4!M?= z89vAWQwQJs>dIb?gv>Oy`d2(cd?`{wWCEy2KY$q~B|y79a>Bf=ccZf4b`Y5D7Bz;( z5Yp28jg(%kYOj92Uy5TXxFpQ5SMQecdcPO*MxszRsG zp-?#O@39Vv-px7|ZuNvB zF63|o82k;m*v(emTqdbwu-!YiKn;-mI&X20!OAyi=l+>6lHm%_1;`0h?x?WrQDWN^ zV{+uPYarXCy=Kwzwed@(J){WmJGf+`qGd}*P(!xDDL4f)Io+nW1w|uo-bT@-^RaQz zE)^kU;B4T+bJ#1C58U+$qw)O|zVdi4hpwXZ2_t!^B?nKeOUCFLNBUku(o&2#xXc_d z*OlxeQ?TBUBC~Uaoj5XmC_#CODg|`U-ee}0=fZkK0edW=jrjpQ=RQ1xIJ#|E(u>_7UP93kcaCfdJj{^h*=OLiE_s

>MvLEeZG5{N~AiDkTpjT3W8TZq&9cZ98>lyt2H90nn* zD;t0(l&ylN!4NbccMV85;sI}eT!N&X@rbfP?72f(yS>jij{G;SK*6-;jNx;`MhA9T zQ*e(uuIgS9Qh=y2{Zrx;p|?v=B;L+IGjRYf#a{CKd7zNQdx1vVfAHF`86gd7ADQ=h z6$c>#BybBg$wZqAm`Sx%?4R501K#H1cjJP<{758T4kRRQMlaR)A)vo>2qB?&*#ANk z5pUKPPk{m?+WUh^k$l;yBBgbhS?Tgc5HS^!?SZ9P>A*xAU{Zbf*#ly{IwH~;p(3C! z7Kt%gNL1ZCV@zqzXvA~8`j`AHbhRcNy;xRXe&QDCYcW`g@>dM$63X6Lqx!bs$7y60 zq^-}NR)PDkn1N{&ZK3d2MSwr}SsP*iaI_)p=4iW%xN#Kz5C&FFUMSJx182MRv+Xea zQ9qzs8C3&;9eq9BuuUvTn6ohUY+CiS2nZrcVm+PEP1^%?a1wDxJ>KGQf2#W6W{FH7 znuqcH#tW&a#h8Fu57p;eA)gi;nJtsoa@#O1Dx9?(_U1!g;eNPCHu_q!a2Bj1{>#E8| zlquncaOJv%5j#XU^&XrWtJ4S}mW4H+djNQ7>AI<(XEt&m?zn0e!fd|gOtGMonYPza zSRK0^P^OdT%pA$XBIm>|72gHwEJKdh&bQC7o+yuw7gC>t~wSif-lxtprJjk~VxEXS_<{WbOJ z3eV1Hzw|Yy0HHwP>e$WbPA$x;&lL)uS9a0wMg4-tEypf*9SpCb@ld_~Tu6Id`LJ(q zrzbxUiouZo(pxP5fxKd3;9&gUCQmOle~S2Q{{xt~k&u-IyACt=4Cj>`wJcj$twoge z&LZL${T^jOkk2Y|FA-$5G)p}xqCfkG>yWU7yYs&0pY4JP;%8s0g%c;lMF>l z0Ei3~E>k?`{P~;YQE04eRjS9qZC$(%AAg+L-~g*gi)qwL7KeGj(Aq%RugvJ(!?0kK zJ?pzI`W*5^C8J~r&&+H>?+b>8{EF5+IqH^8PU>l5_;CAV{W)92x8vJk)%Lvmv4}G)0oJLN(t@=vRincqL4<{K|k-(NE~! zJ4R9q=|qsaw%UjcdLIXJDD?C4L?0cr1S!6pFz&{lPN@CigGv@ytV`4Vv9j~R=X%0> zM}jF3hv6&cv4xECUw_r)?jM4|M|VL!EmqjKzu5lLa4Z*X7hofQdyV!ja)XhPSmy5s zq$!{_O^(ViGO28<-dFa<43CA~nCYlBbpvu4MMv)~-IAifSWk`O z4LR%VFPUTxo$zd6$jh_i0F=~uq3R3)OMSKD9E$3%-kfyQAn58ADlC&;grU*tfrMch zlK*8l@G{cz%pBAudy>wIiYtP+V z=5^T{1BbA3`If3YUk;;Ku4IA74>e2r<={c~+Jmu+su%3r0-NcIm5lS@GDSp~98|gg zjjVczB@mN#I3R5@t#IT)=r|46eXOXYWz=Dp3A#6RXgm*0zazukX15n1-FIg*h%QMB z%riOkej}w@clS*%x4Y+3z^6c>(wAXUn% zDD){CqrJENv=?EJaQH3$76HnPL7sviu`u@WJHK+@tg$-ktbQeegH)|H=pvSv4ns#3 z7k7rAD-(dWYdX=3i}+`A`FsFI07JP8VG;rKHocDNFOt-FY7^gj62o3NFbnjpSoOx4 zBvy$3?Ns=+AQF-k!V|Nh&_Po|4nXvol`0mshVwx#8>@z3xW<6f=FJmr*KnX1rMbjk z?Rmb?V7VmKriIc`-9wHOxNbGhq!w?sleR;vQMW;e;V+{X3hYZ{xo$&u8M6>e>(335 zqWtQnZEa&VnuP$l@$6mHr9e&9Fv~qKL1Yn6EHWwt&P76LFF!;NM$kH-$)4@I*0XF7UbUf*lgiF9u1rf7jtc)XhftsW zqN%dfWCcUYCv#*&UYN1b02C4u#Tp;ZxC46IyefpNP%pFUL`frr3=8~tHmGEipFkmy zBQdAfUFzSAj}+_<>o1B3Gl=R~gX+kWZ*&EwW&{MOKO}s-X`uVOsblOy1oA_AA70CKkm<7o%b zsR6kg%rFN_Mco|fZ64HA=mmOp_cs=q_VaQ)GF%zDR>gRcTqM9>aKbjS<%Z)tcp3qk zA9S=rx9=pLjN�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

`au*`G2Yr%N;qYN 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�^kk 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�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

Z~GnQkOSsn3W6pNlwzq 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 Vk3&`!c{w!&MOHB}19L;x{{RYqVe|k1 literal 0 HcmV?d00001 diff --git a/source_code/Half_Precision_FPU/addition/float_add_16bit.sv b/source_code/Half_Precision_FPU/addition/float_add_16bit.sv new file mode 100644 index 000000000..b5f712a8b --- /dev/null +++ b/source_code/Half_Precision_FPU/addition/float_add_16bit.sv @@ -0,0 +1,198 @@ +module top ( + // I/O ports + input logic hz100, reset, + input logic [20:0] pb, + output logic [7:0] left, right, + ss7, ss6, ss5, ss4, ss3, ss2, ss1, ss0, + output logic red, green, blue, + + // UART ports + output logic [7:0] txdata, + input logic [7:0] rxdata, + output logic txclk, rxclk, + input logic txready, rxready +); + reg[15:0] float1, float2, expected_sum, sum; + assign float2 = {1'b0, 5'b10101, 10'b0011001100}; + assign float1 = {1'b0, 5'b10101, 10'b0011100011}; + assign expected_sum = {1'b0, 5'b11011, 10'b0111011100}; + + half_FP_add add1(.clk(hz100), .rst(1'b0), .float1(float1), .float2(float2), .sum_out(sum)); + + assign blue = (sum == expected_sum) ? 1'b1 : 1'b0; + assign left = sum[15:8]; + assign right = sum[7:0]; + + +endmodule + +// Add more modules down here... + +module half_FP_add ( + input clk, rst, + input [15:0] float1, float2, + output[15:0] sum_out +); + logic sign1, sign2, sign_s, hidden_bit1, hidden_bit2; + logic [4:0] exp1, exp2, exp_s; + logic [9:0] mant1, mant2, mant_s; + logic [13:0] sum; + logic guard, round_bit, sticky; + + typedef enum logic [2:0] { + initializing, + special_case, + align, + add, + normalise_1, + normalise_2, + round, + assemble + } state_t; + + state_t state; + state_t next_state; + + always_comb begin + case(state) + initializing: begin + sign1 = float1[15]; + sign2 = float2[15]; + + exp1 = float1[14:10]; + exp2 = float2[14:10]; + + mant1 = float1[9:0]; + mant2 = float2[9:0]; + hidden_bit1 = 1'b1; + hidden_bit2 = 1'b1; + + next_state = special_case; + end + + special_case: begin + next_state = align; + end + + align: begin + if(exp1 > exp2) begin + exp2 = exp2 + 1; + mant2 = mant2 >> 1; + end + else if(exp2 > exp1) begin + exp1 = exp1 + 1; + mant1 = mant1 >> 1; + end + else begin + next_state = add; + end + end + + add: begin + exp_s = exp1; + if(sign1 == sign2) begin + sum = {4'b0000, mant1 + mant2}; + sign_s = sign1; + end + else begin + if(mant1 >= mant2) begin + sum = {4'b0000, mant1 - mant2}; + sign_s = sign1; + end + else begin + sum = {4'b0000, mant2 - mant1}; + sign_s = sign2; + end + end + + if(sum[9]) begin + mant_s = sum[13:4]; + guard = sum[3]; + round_bit = sum[2]; + sticky = sum[1] | sum[0]; + exp_s = exp_s + 1; + end + else begin + mant_s = sum[12:3]; + guard = sum[2]; + round_bit = sum[1]; + sticky = sum[0]; + end + + next_state = normalise_1; + end + + normalise_1: begin + if(mant_s[9] == 0 && exp_s > 0) begin + exp_s = exp_s - 1; + mant_s = mant_s << 1; + mant_s[0] = guard; + guard = round_bit; + round_bit = 0; + end + else begin + next_state = normalise_2; + end + end + + normalise_2: begin + if(exp_s == 0) begin + exp_s = exp_s + 1; + mant_s = mant_s >> 1; + guard = mant_s[0]; + round_bit = guard; + sticky = sticky | round_bit; + end + else begin + state = round; + end + end + + round: begin + if(guard && (round_bit | sticky | mant_s[0])) begin + if(mant_s == 10'b1111111111) begin + exp_s = exp_s + 1; + end + end + next_state = assemble; + end + + assemble: begin + sum_out[15] = sign_s; + sum_out[14:10] = exp_s; + sum_out[9:0] = mant_s; + end + + default: begin + sign_s = 0; + sign1 = 0; + sign2 = 0; + + exp_s = 0; + exp1 = 0; + exp2 = 0; + + hidden_bit1 = 0; + hidden_bit2 = 0; + + mant_s = 0; + mant1 = 0; + mant2 = 0; + + guard = 0; + round_bit = 0; + sticky = 0; + sum = 0; + + next_state = state; + end + endcase + end + + always_ff @(posedge clk, posedge rst) begin + if(rst == 1'b1) begin + state <= initializing; + end + else state <= next_state; + end +endmodule diff --git a/source_code/Half_Precision_FPU/multiplication/float_mult_16bit.sv b/source_code/Half_Precision_FPU/multiplication/float_mult_16bit.sv new file mode 100644 index 000000000..15658e478 --- /dev/null +++ b/source_code/Half_Precision_FPU/multiplication/float_mult_16bit.sv @@ -0,0 +1,206 @@ +module float_mult_16bit ( + input logic clk, n_rst, + input logic [15:0] float1, + input logic [15:0] float2, + output logic [15:0] product +); + + logic sign1, sign2, sign_p; + logic hidden_lead1, hidden_lead2; + + logic signed_exponent; + logic [4:0] exp1, exp2; + logic [4:0] exp_p; + logic signed [4:0] exp_p_signed; + + logic [5:0] exp_comb; + logic [9:0] mant1, mant2, mant_p; + + logic [21:0] mant_multiplied; + logic guard, round_bit, sticky, dummy; + + typedef enum logic [2:0] { + initializing, + special_case, + multiply, + normalize_exp, + normalize_mant, + assemble + } state_t; + + state_t state; + state_t next_state; + + always_comb begin + case(state) + // Currently goes + // initializing -> + // special_case -> + // multiplying -> + // normalize_exp -> + // normalize_mant -> + // assemble + initializing: begin + sign1 = float1[15]; + sign2 = float2[15]; + + exp1 = float1[14:10]; + exp2 = float2[14:10]; + + hidden_lead1 = 1'b1; + hidden_lead2 = 1'b1; + + mant1 = float1[9:0]; + mant2 = float2[9:0]; + product = 'd0; + + next_state = special_case; + end + + special_case: begin + // if either number is infinity, output is infinity + if((exp1 == 5'b11111 & mant1 == 0) | (exp2 == 5'b11111 & mant2 == 0)) begin + sign_p = sign1 ^ sign2; // Accounts for +/- infinity + exp_p = 5'b11111; + mant_p = 10'b0000000000; + next_state = assemble; + end + + // if either number is quiet NaN, return quiet Nan + else if((exp1 == 5'd31 & mant1[9] == 1'b0) | (exp2 == 5'd31 & mant2[9] == 1'b0)) begin + sign_p = 1'b1; + exp_p = 5'b11111; + mant_p = 10'b0111111111; + next_state = assemble; + end + + // if either number is signalling NaN, return signalling Nan + else if((exp1 == 5'd31 & mant1[9] == 1'b1) | (exp2 == 5'd31 & mant2[9] == 1'b1)) begin + sign_p = 1'b1; + exp_p = 5'b11111; + mant_p = 10'b1111111111; + next_state = assemble; + end + + // if either number is zero, return zero + else if((exp1 == 0 & mant1 == 0) | (exp2 == 0 & mant2 == 0)) begin + sign_p = sign1 ^ sign2; // IEE754 mandates signed zero + exp_p = 5'b00000; + mant_p = 10'b0000000000; + next_state = assemble; + end + + else begin + if(exp1 == 0) begin + hidden_lead1 = 1'b0; + end + if(exp2 == 0) begin + hidden_lead2 = 1'b0; + end + next_state = multiply; + end + end + + multiply: begin + sign_p = sign1 ^ sign2; + + if((exp1 + exp2) < 15) begin + exp_p = 0; + exp_p_signed = exp1 + exp2 - 5'd15; + signed_exponent = 1'b1; + end + else begin + exp_p = exp1 + exp2 - 5'd15; + exp_p_signed = 0; + signed_exponent = 1'b0; + end + + mant_multiplied = {hidden_lead1, mant1} * {hidden_lead2, mant2}; + + if(signed_exponent) begin + next_state = normalize_exp; + mant_multiplied = mant_multiplied >> 1; + end + else begin + next_state = normalize_mant; + end + end + + normalize_exp: begin + if(exp_p_signed < 0) begin + exp_p_signed = exp_p_signed + 1; + mant_multiplied = mant_multiplied >> 1; + end + else begin + next_state = normalize_mant; + end + end + + normalize_mant: begin + if(mant_multiplied[21] == 1'b1) begin + exp_p = exp_p + 1; + mant_multiplied = mant_multiplied >> 1; + end + + mant_p = mant_multiplied[19:10]; + + guard = mant_multiplied[9]; + round_bit = mant_multiplied[8]; + + if(guard) begin + round_bit = guard; + guard = mant_p[0]; + mant_p = mant_p + 1; + end + + next_state = assemble; + end + + assemble: begin + product[15] = sign_p; + product[14:10] = signed_exponent ? exp_p_signed : exp_p; + product[9:0] = mant_p; + + // if (float1 || float2) next_state = initializing; + end + + default: begin + sign_p = 0; + sign1 = 0; + sign2 = 0; + + exp_p = 0; + exp_p_signed = 0; + exp1 = 0; + exp2 = 0; + + hidden_lead1 = 0; + hidden_lead2 = 0; + + mant_p = 0; + mant1 = 0; + mant2 = 0; + + guard = 0; + round_bit = 0; + sticky = 0; + mant_multiplied = 0; + + next_state = state; + end + endcase + end + + // always_comb begin + // product[15] = sign_p; + // product[14:10] = signed_exponent ? exp_p_signed : exp_p; + // product[9:0] = mant_p; + // end + + always_ff @(posedge clk, negedge n_rst) begin + if(!n_rst) begin + state <= initializing; + end + else state <= next_state; + end +endmodule