diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 175e66a54..48225986c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,8 @@ jobs: graphviz \ bc \ ghdl \ - iverilog + iverilog \ + verilator - name: run: | diff --git a/openasip/data/ProGe/cvxif_coprocessor.sv.tmpl b/openasip/data/ProGe/cvxif_coprocessor.sv.tmpl new file mode 100644 index 000000000..47e2cd09a --- /dev/null +++ b/openasip/data/ProGe/cvxif_coprocessor.sv.tmpl @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2025 Tampere University. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +module FUNAME_coprocessor + import cvxif_sup_pkg::*; +#( + // CVXIF Types According to the CVA6 + parameter int unsigned NrRgprPorts = 3, + parameter type readregflags_t = logic, + parameter type writeregflags_t = logic, + parameter type id_t = logic, + parameter type hartid_t = logic, + parameter type x_compressed_req_t = logic, + parameter type x_compressed_resp_t = logic, + parameter type x_issue_req_t = logic, + parameter type x_issue_resp_t = logic, + parameter type x_register_t = logic, + parameter type x_commit_t = logic, + parameter type x_result_t = logic, + parameter type cvxif_req_t = logic, + parameter type cvxif_resp_t = logic, + localparam type registers_t = logic [NrRgprPorts-1:0][31:0] +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input cvxif_req_t cvxif_req_i, + output cvxif_resp_t cvxif_resp_o +); + + //Compressed interface + logic x_compressed_valid_i; + logic x_compressed_ready_o; + x_compressed_req_t x_compressed_req_i; + x_compressed_resp_t x_compressed_resp_o; + //Issue interface + logic x_issue_valid_i; + logic x_issue_ready_o; + x_issue_req_t x_issue_req_i; + x_issue_resp_t x_issue_resp_o; + //Commit interface + logic x_commit_valid_i; + x_commit_t x_commit_i; + // Register interface signals + x_register_t register; + logic register_valid; + //Result interface + logic x_result_valid_o; + logic x_result_ready_i; + x_result_t x_result_o; + + assign x_compressed_valid_i = cvxif_req_i.compressed_valid; + assign x_compressed_req_i = cvxif_req_i.compressed_req; + assign x_issue_valid_i = cvxif_req_i.issue_valid; + assign x_issue_req_i = cvxif_req_i.issue_req; + assign x_commit_valid_i = cvxif_req_i.commit_valid; + assign x_commit_i = cvxif_req_i.commit; + assign x_result_ready_i = cvxif_req_i.result_ready; + assign register = cvxif_req_i.register; + assign register_valid = cvxif_req_i.register_valid; + + assign cvxif_resp_o.compressed_ready = x_compressed_ready_o; + assign cvxif_resp_o.compressed_resp = x_compressed_resp_o; + assign cvxif_resp_o.issue_ready = x_issue_ready_o; + assign cvxif_resp_o.issue_resp = x_issue_resp_o; + assign cvxif_resp_o.result_valid = x_result_valid_o; + assign cvxif_resp_o.result = x_result_o; + assign cvxif_resp_o.register_ready = x_issue_ready_o; + + //Compressed interface handler/decoder + cvxifcompressed_decoder #( + .x_compressed_req_t (x_compressed_req_t), + .x_compressed_resp_t(x_compressed_resp_t) + ) compressed_decoder_i ( + .clk_i (clk_i), //No current use + .compressed_valid (x_compressed_valid_i), + .x_compressed_req (x_compressed_req_i), + .x_compressed_resp (x_compressed_resp_o), + .compressed_ready (x_compressed_ready_o) + ); + + logic[cvxif_sup_pkg::NConfigbits_C-1 : 0] configbits_in_i; // For input config bits + logic x_result_valid_i; // For intermediate result valid + logic[cvxif_sup_pkg::NConfigbits_C-1 : 0] configbits_o; // For config bits out the FU + logic[cvxif_sup_pkg::X_RFW_WIDTH-1 : 0] result_data_out; // For data out from the function unit + logic instr_accept, instr_ready; + logic[31:0] instruction_in; + CONFIG_DEFINE + assign x_issue_ready_o = instr_ready && register.rs_valid[0] && register.rs_valid[1] && (NrRgprPorts == 3 ? register.rs_valid[2] : 1'b1); + assign x_issue_resp_o.accept = instr_accept; + assign instruction_in = cvxif_sup_pkg::OpcodeMask & x_issue_req_i.instr; + assign x_result_valid_i = x_issue_valid_i & x_issue_ready_o & instr_accept; + assign configbits_in_i = {x_result_valid_i, x_issue_req_i.instr[11:7], x_issue_resp_o.writeback, x_issue_req_i.hartid, x_issue_req_i.id}; + + fu_FUNAME function_unit_i ( + .clk(clk_i), + .rstx(rst_ni), + .operation_enable_in(x_issue_valid_i), + .result_ready_in(x_result_ready_i), + .configbits_in(configbits_in_i), + .operation_in(instruction_in), + CONFIG_ENINPUT1.data_OUTPUTF_out(result_data_out), + CONFIG_BITS.configbits_out(configbits_o), + .accept_o(instr_accept), + .ready_o(instr_ready) + ); + + always_comb begin + x_issue_resp_o.writeback = 1'b0; + if (instr_accept) begin + x_issue_resp_o.writeback = 1'b1; + end + end + + instr_tracker_FUNAME #( + .IdWidth(cvxif_sup_pkg::X_ID_WIDTH), + .HWidth(cvxif_sup_pkg::X_HARTID_WIDTH), + .IdBits(cvxif_sup_pkg::IdBits) + ) instruction_tracker_i ( + .clk(clk_i), + .rstx(rst_ni), + .commit_hartid_i(x_commit_i.hartid), + .commit_id_i(x_commit_i.id), + .commit_kill_i(x_commit_i.commit_kill), + .commit_valid_i(x_commit_valid_i), + .issue_hartid_i(x_issue_req_i.hartid), + .issue_id_i(x_issue_req_i.id), + .issue_valid_i(x_issue_resp_o.accept), + SEARCH_CONFIGOUT_COMMIT_TRACKER.output_hartid_i(configbits_o[cvxif_sup_pkg::X_ID_WIDTH + cvxif_sup_pkg::X_HARTID_WIDTH - 1 : cvxif_sup_pkg::X_ID_WIDTH]), + .output_id_i(configbits_o[cvxif_sup_pkg::X_ID_WIDTH - 1 : 0]), + .output_valid_i(configbits_o[cvxif_sup_pkg::NConfigbits_C-1]) + ); + + always_comb begin + x_result_valid_o = configbits_o[cvxif_sup_pkg::NConfigbits_C-1]; + x_result_o.id = configbits_o[cvxif_sup_pkg::X_ID_WIDTH - 1 : 0]; + x_result_o.rd = configbits_o[cvxif_sup_pkg::NConfigbits_C-2 : cvxif_sup_pkg::NConfigbits_C - 1 - 5]; + x_result_o.we = configbits_o[cvxif_sup_pkg::X_ID_WIDTH + cvxif_sup_pkg::X_HARTID_WIDTH + cvxif_sup_pkg::X_DUALWRITE : cvxif_sup_pkg::X_ID_WIDTH + cvxif_sup_pkg::X_HARTID_WIDTH] & x_result_valid_o; + x_result_o.hartid = configbits_o[cvxif_sup_pkg::X_ID_WIDTH + cvxif_sup_pkg::X_HARTID_WIDTH - 1 : cvxif_sup_pkg::X_ID_WIDTH]; + x_result_o.data = result_data_out; + end + +endmodule diff --git a/openasip/data/ProGe/cvxifcompressed_decoder.sv.tmpl b/openasip/data/ProGe/cvxifcompressed_decoder.sv.tmpl new file mode 100644 index 000000000..440c84c0f --- /dev/null +++ b/openasip/data/ProGe/cvxifcompressed_decoder.sv.tmpl @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Tampere University. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +//Module for the compressed interface handling + +module cvxifcompressed_decoder +#( + parameter type x_compressed_req_t = logic, + parameter type x_compressed_resp_t = logic +) ( + input logic clk_i, //Might not need a clock since the response should be given on the same cycle as the input. + input logic compressed_valid, + input x_compressed_req_t x_compressed_req, + output x_compressed_resp_t x_compressed_resp, + output logic compressed_ready +); + //TODO Check the received compressed one is identifiable(x_compressed_req_t.instr) by checking through the compressed instr package + //Output the relevant 32bit version on x_compressed_resp_t.instr and make x_compressed_resp_t.accept while compressed_ready asserted. + //CPU can change all the values until the compressed_ready=1(CPU can retract as well, so the compressed decode task should be combinational) + //Currently rejects all the incoming compressed requests. + + always_comb begin + if (compressed_valid) begin + x_compressed_resp.accept = '0; + compressed_ready = '1; + end + else begin + x_compressed_resp.accept = '0; + compressed_ready = '0; + end + end + +endmodule + + diff --git a/openasip/data/ProGe/rocc_copro.sv.tmpl b/openasip/data/ProGe/rocc_copro.sv.tmpl new file mode 100644 index 000000000..6d50c21dd --- /dev/null +++ b/openasip/data/ProGe/rocc_copro.sv.tmpl @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2025 Tampere University. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +module FUNAME_coprocessor + #( parameter xLen = 64, + PRV_SZ = 2 + ) + ( input clock, + input reset, + output cmd_ready, + input cmd_valid, + input [6:0] cmd_bits_inst_funct, + input [4:0] cmd_bits_inst_rs2, + input [4:0] cmd_bits_inst_rs1, + input cmd_bits_inst_xd, + input cmd_bits_inst_xs1, + input cmd_bits_inst_xs2, + input [4:0] cmd_bits_inst_rd, + input [6:0] cmd_bits_inst_opcode, + input [xLen-1:0] cmd_bits_rs1, + input [xLen-1:0] cmd_bits_rs2, + input cmd_bits_status_debug, + input cmd_bits_status_cease, + input cmd_bits_status_wfi, + input [63:0] cmd_bits_status_isa, + input [PRV_SZ-1:0] cmd_bits_status_dprv, + input cmd_bits_status_dv, + input [PRV_SZ-1:0] cmd_bits_status_prv, + input cmd_bits_status_v, + input cmd_bits_status_sd, + input [22:0] cmd_bits_status_zero2, + input cmd_bits_status_mpv, + input cmd_bits_status_gva, + input cmd_bits_status_mbe, + input cmd_bits_status_sbe, + input [1:0] cmd_bits_status_sxl, + input [1:0] cmd_bits_status_uxl, + input cmd_bits_status_sd_rv32, + input [7:0] cmd_bits_status_zero1, + input cmd_bits_status_tsr, + input cmd_bits_status_tw, + input cmd_bits_status_tvm, + input cmd_bits_status_mxr, + input cmd_bits_status_sum, + input cmd_bits_status_mprv, + input [1:0] cmd_bits_status_xs, + input [1:0] cmd_bits_status_fs, + input [1:0] cmd_bits_status_vs, + input [1:0] cmd_bits_status_mpp, + input [0:0] cmd_bits_status_spp, + input cmd_bits_status_mpie, + input cmd_bits_status_ube, + input cmd_bits_status_spie, + input cmd_bits_status_upie, + input cmd_bits_status_mie, + input cmd_bits_status_hie, + input cmd_bits_status_sie, + input cmd_bits_status_uie, + input resp_ready, + output resp_valid, + output [4:0] resp_bits_rd, + output [xLen-1:0] resp_bits_data, + output busy + ); + + localparam [4:0] bits_reg = 5'b00000; + + logic [63:0] inter_rs1; + logic [63:0] inter_rs2; + logic [6:0] inter_opcode; + logic [6:0] inter_func; + logic [4:0] inter_rd; + + reg [63:0] inter_rs1_r; + reg [63:0] inter_rs2_r; + reg [31:0] inter_opcode_r; + reg [6:0] inter_func_r; + reg [4:0] inter_rd_r; + reg inter_en_r; + reg inter_cmd_valid; + + assign busy = '0; // Expects the core always accepts the data from the coprocessor + assign cmd_ready = '1; + assign inter_rs1 = cmd_bits_rs1; + assign inter_rs2 = cmd_bits_rs2; + assign inter_opcode = cmd_bits_inst_opcode; + assign inter_func = cmd_bits_inst_funct; + assign inter_rd = cmd_bits_inst_rd; + assign inter_cmd_valid = cmd_valid; + + // CMD Control COMB + always_comb begin + inter_rs1_r = '0; + inter_rs2_r = '0; + inter_opcode_r = '0; + inter_rd_r = '0; + inter_en_r = '0; + if (inter_cmd_valid == 1) begin // Valid from the ROCC + inter_rs1_r = inter_rs1; + inter_rs2_r = inter_rs2; + inter_opcode_r = {inter_func, bits_reg, bits_reg, cmd_bits_inst_xd, cmd_bits_inst_xs1, cmd_bits_inst_xs2, bits_reg, inter_opcode}; + inter_rd_r = inter_rd; + inter_en_r = '1; + end + end + + // FU + fu_FUNAME fu_functionunit_i ( + .clk(clock), + .rstx(~reset), + .glock_in(0), + .operation_in(inter_opcode_r), + .data_P1_in(inter_rs1_r[31:0]), + .operation_enable_in(inter_en_r), + .data_P2_in(inter_rs2_r[31:0]), + .configs_in({inter_rd_r,inter_en_r}), + .configs_out(resp_bits_rd), + .out_ready(resp_ready), + .out_valid(resp_valid), + .data_P3_out(resp_bits_data[31:0]) + ); + +endmodule diff --git a/openasip/doc/man/OpenASIP/OpenASIP.tex b/openasip/doc/man/OpenASIP/OpenASIP.tex index 8f78cacff..2e86dc42c 100644 --- a/openasip/doc/man/OpenASIP/OpenASIP.tex +++ b/openasip/doc/man/OpenASIP/OpenASIP.tex @@ -3581,6 +3581,7 @@ \subsection{Tour to Using the Hardware Loops} \section{RISC-V Tutorial} +\label{sec:riscvtute} This tutorial goes through the RISC-V customization in OpenASIP toolset. It starts from C code and ends up with a VHDL of a customized processor. During the tutorial the user will learn how to add custom instructions to a @@ -3625,7 +3626,7 @@ \subsection{The Sample Application} lookup table. This is a quite usual method of algorithm optimization. \subsection{Starting Point Processor Architecture} - +\label{subsec:startarc} Copy the \file{rv32im.adf} file included in OpenASIP distribution to a new ADF file: @@ -4149,6 +4150,236 @@ \subsection{Final Words} For your own designs, we recommend that you use the included \file{rv32im.adf} description as a base and add your own custom instructions on top of it. +\section{RISC-V Coprocessor generation} +\label{sec:coproriscv} + +This section eloborates the process of generating coprocessors with the \textit{generatecoprocessor} utilizing the same files as in section~\ref{sec:riscvtute} and RTL simulation of them with supported host cores. + +Tutorial files should be downloaded and extracted as in the above section, however, the same procedure for it, is mentioned below. + +\begin{verbatim} + > wget http://openasip.org/tutorial_files/tce_tutorials.tar.gz + > tar -xzf tce_tutorials.tar.gz + > cd tce_tutorials/tce_tour + > ls -la + + total 84 + drwxr-xr-x 3 tce tce 4096 2010-05-28 11:40 . + drwx------ 7 tce tce 4096 2012-05-18 13:22 .. + -rw------- 1 tce tce 5913 2010-03-08 20:01 crc.c + -rw------- 1 tce tce 1408 2008-11-07 11:35 crc.h + -rw------- 1 tce tce 3286 2008-11-07 11:35 crcTable.dat + -rw-r--r-- 1 tce tce 2345 2010-03-08 13:04 custom_operation_behavior.cc + -rw-r--r-- 1 tce tce 855 2010-05-28 11:41 custom_operations.idf + -rw------- 1 tce tce 1504 2010-03-08 20:01 main.c + -rw-r--r-- 1 tce tce 45056 2010-03-10 16:09 tour_example.hdb + drwxr-xr-x 2 tce tce 4096 2010-05-28 11:40 tour_vhdl +\end{verbatim} + +The custom operation used in this tutorial is \textit{crc\_xor\_shift} which is already made in the above section. The necessary steps for adding the custom instruction as a DAG operation, is as below. + +\paragraph{Using Operation Set Editor (OSEd) to add the operation data.} + +OSEd is started with the command + +\shellcmd{osed \&} + +Create a new operation module, which is a container for a set of operations. +You can add a new module in any of the predefined search paths, provided +that you have sufficient file system access permissions. + +For example, choose directory +\file{/home/\emph{user}/.openasip/opset/custom}, where \emph{user} is the +name of the user account being used for the tutorial. This directory +is intended for the custom operations defined by the current user, and +should always have sufficient access rights. + +\begin{figure} + \begin{center} \includegraphics[width=0.8\textwidth]{eps/osed_crc_xor_shift.eps} + \caption{Operation Set Editor when adding new operation CRC\_XOR\_SHIFT.} + \label{fig:osed_crc_xor_shift} \end{center} +\end{figure} + + +\begin{enumerate} +\item% + Click the root in the left area of the main window which opens list + of paths. Right-click on a path name + \file{/home/\emph{user}/.openasip/opset/custom}. A drop-down menu appears + below the mouse pointer. +\item% + Select \textbf{Add module} menu item. +\item% + Type in the name of the module (for example, `riscv\_tutorial') and + press \emph{OK}. The module is now added under the selected path. +\end{enumerate} + +\paragraph{Adding the new operations.} We will now add the operation +definitions to the newly created operation module. + +\begin{enumerate} +\item% + Select the module that you just added by right-clicking on its name, + displayed in the left area of the main window. A drop down menu appears. +\item% + Select \textbf{Add operation} menu item. +\item% + Type `CRC\_XOR\_SHIFT' as the name of the operation. +\item% + Add two inputs by pressing the \emph{Add} button under the operation input + list. Select \textit{UIntWord} as type. +\item% + Add one output by pressing the \emph{Add} button under the operation output + list. Select \textit{UIntWord} as type. +\item% + At this stage the dialog should look like in Fig.~\ref{fig:osed_crc_xor_shift} +\item% + Press \emph{Open DAG} +\item% + Type the operation description in the dialog as a DAG by combining an SHRU + and XOR operation, see Fig.~\ref{fig:crc_dag}. +\item% + Press \emph{Save} and then \emph{OK}. +\item% + Close the dialog by pressing the \emph{OK} button. A confirmation dialog + will pop up. Press \emph{Yes} to confirm the action. + The operation definition is now added to the module. +\end{enumerate} + +\begin{figure} + \begin{center} + \includegraphics[width=12cm]{eps/crc_dag.eps} + \caption{CRC\_XOR\_SHIFT DAG description} + \label{fig:crc_dag} + \end{center} +\end{figure} + + +Next, make a new copy of the \file{rv32im.adf} file included in OpenASIP distribution to a new ADF +file: + +\begin{verbatim} + cp $(openasip-config --prefix)/share/openasip/data/mach/rv32im.adf coprogen.adf +\end{verbatim} + +Now open coprogen.adf with ProDe to add the new custom operation to your architecture definition file as a custom instruction within a function unit. + +\shellcmd{prode coprogen.adf} + +\begin{enumerate} + \item% + Right click on the raster (alternatively, go to edit from the menu) select \textbf{Add}, from the drop down menu. + \item% + Select \textbf{Function Unit...} from the secondary drop down menu. + \item% + Type the Name as \textit{custom} + \item% + Add three ports: two input and one output by clicking \textit{add} three times in the Ports section. + \item% + Click \emph{add from Opset} in FU dialog box. + \item% + Filter CRC\_XOR\_SHIFT. + \item% + Click the operation and press \emph{OK} + \item% + On the Operand usage view, you can change the desired latency by switching + the third operand write cycle. For this case, you can leave it as it is because + the operation is simple and can be therefore easily implemented in a single cycle. + \item% + Press \emph{OK} on the operation dialog. + \item% + Press \emph{OK} on the FU dialog. +\end{enumerate} + +Now you have successfully added the operation to the microarchitecture. We need to also add it to a RISC-V instruction format. + +\begin{enumerate} + \item% + Press \emph{OTA Formats} under \emph{Edit}. + The formats have predefined names that are included in this + architecture definition. The compiler and ProGe expects these names to be + found in the definition. + \item% + Click the \emph{riscv\_r\_type} item. + The operations included in this format are listed in the operations menu, + RISC-V naming scheme is used for the operations. + \item% + Click \emph{Add} + \item% + Click \emph{crc\_xor\_shift} + \item% + Click \emph{OK} on the operation dialog. + \item% + Click \emph{OK} on the OTA Formats dialog. + \item% + Save the ADF by pressing Ctrl-S or by clicking the save icon and close ProDe. +\end{enumerate} + +Now the custom instruction has been succefully added to the architecture file. In the next subsection coprocessor generation and simulation for each of the interfaces are eloborated seperately. + +\subsection{CV-X interface} + +For the CV-X interface, the CORE-V CVA6 is used as the compatible RISC-V host core. To continue with the tutorial, cloned repository of the CVA6 as well as the tools mandatory for simulating CVA6, should have to be present. The neccessary files and the quick setup guide for CVA6, can be found on the link: +\url{https://github.com/openhwgroup/cva6} +(Steps 1 to 7 in the quick setup must be followed). + +Following command generates the hardware description files for the coprocessor with CV-X interface. + +\shellcmd{generatecoprocessor -o out\_cvx -c cvx coprogen.adf} + +Above command produces the CV-X-IF compatible coprocessor RTL files in the path \file{out\_cvx/systemverilog} + +Next, the generated hardware files need to be integrated with the CVA6 host core. For that, modifications are needed for two files in the CVA6, + +\begin{enumerate} + \item% + Directory paths mentioned under \textit{//CVXIF} in the file \file{cva6/core/Flist.cva6} need to be replace with the directory paths of the generated RTL files. + \item% + The name of the \textit{cvxif\_example\_coprocessor} in the file \file{cva6/corev\_apu/src/ariane.sv} have to be replaced with the given name to the coprocessor. + \item% + As the \textit{oacc\_riscv} compiler can generate binaries targetting the custom operations, \textit{oacc\_riscv} needs to be configured into the compilation function in the file \file{cva6/verif/sim/cva6.py} +\end{enumerate} + +Executing the following steps will copy the generated RTL files into the CVA6 and modify the neccessary files. + +\shellcmd{export cva6\_path=\{\}} + +\shellcmd{export OPENASIP\_PATH=\{\}} + +\shellcmd{export INTERFACE=1} + +\shellcmd{cp OPENASIP\_PATH/testsuite/systemtest/scripts/coproGen\_tour/coproGen\_tour.f coproGen\_tour.sh} + +\shellcmd{bash coproGen\_tour.sh} + +The script \file{coproGen\_tour.sh} compiles \file{crc.c} and \file{main.c} files utilizing the \textit{oacc\_riscv} compiler targetting the \textit{coprogen.adf} and executes the CVA6 simulation with Verilator. + + +\subsection{RoCC interface} + +For the RoCC Interface, Rocket-chip in the chipyard repository, is used as the compatible RISC-V host core in this tutorial. To proceed further with the tutorial, initial repository setup has to be done according to the following: \url{https://chipyard.readthedocs.io/en/latest/Chipyard-Basics/Initial-Repo-Setup.html}. + +Following command generates the hardware RTL files for the coprocessor that can be integrated with the RoCC interface. + +\shellcmd{generatecoprocessor -o out\_rocc -c rocc coprogen.adf} + +Above command produces files related to the coprocessor in the folder \file{out\_rocc/systemverilog} + +Next, the generated RTL files have to be integrated with the Rocket-chip and neccessary configurations have to be done. + +Executing following commands will copy the generated RTL files and do the appropriate modifications to the chipyard configurations. + +\shellcmd{export ROCC\_PATH=\{\}} + +\shellcmd{export OPENASIP\_PATH=\{\}} + +\shellcmd{export INTERFACE=2} + +\shellcmd{cp OPENASIP\_PATH/testsuite/systemtest/scripts/coproGen\_tour/coproGen\_tour.sh .} + +\shellcmd{bash coproGen\_tour.sh} + + \chapter{PROCESSOR DESIGN TOOLS} \label{chapter:procgen} @@ -5973,6 +6204,49 @@ \subsection{AlmaIFIntegrator} -d onchip -f onchip -e almaif\_tta -p program.tpef proc.adf} +\section{Coprocessor Generator} +\label{sec:gencopro} +Coprocessor Generator is a command line tool for producing synthesizable hardware description of a coprocessor implemented as a special function unit. The coprocessor contains custom RISC-V operations specified by an architecture definition file, is incorporated with a standard interface that can be seamlessly integrated with compatible host cores. + +\textbf{Input}: HDB, ADF + +\textbf{Output}: System Verilog implementation of the coprocessor + +The coprocessor generator is invoked with the following syntax: + +\textit{generatecoprocessor architecture} + +The argument \textit{architecture} is the architecture definition file that contains the custom function unit to be implemented. The architecture definition file could be based on \file{rv32im.adf} (RISC-V starting point architecure, Subsection~\ref{subsec:startarc}) or an architecture definition file contains only the function unit which should be implemented as a coprocessor. + +Currently, Generate coprocessor is capable of producing coprocessor hardware descriptions for two commonly used generalized coprocessor integration interfaces. Option -c accepts keyword 'cvx' for Core-V eXtension interface (CV-X-IF) and 'rocc' for RoCC interface. +Funtion units in the coprocessor can have operations with various latencies including complex DAG operations. + +The generated coprocessors can be integrated with host RISC-V cores that support the relevant integration interface. + + + +\begin{center} +\begin{longtable}[htb]{@{}p{.10\textwidth}@{}p{.20\textwidth}% + @{}p{.65\textwidth}} + +\textbf{Short Name} &\textbf{Long Name} &\textbf{Description} \\ +\hline + +c & \verb|coprointerface| & +Specifies the interface of the coprocessor. \\ + +h & \verb|hdb-list| & +Comma separated list of HDB files for operation implementations.\\ + +o & \verb|output| & +Name of the output directory. If not given, an output directory called +`proge-output' is created inside the current working directory. +\\ +\end{longtable} +\end{center} + + + \section{Hardware Database Editor (HDB Editor)} \label{sec:hdbedit} % TODO: some text, describe at least the most important use cases [VP] diff --git a/openasip/scheduler/testbench/scheduler_tester.py b/openasip/scheduler/testbench/scheduler_tester.py index 9e8f11b91..d3c5d66ab 100755 --- a/openasip/scheduler/testbench/scheduler_tester.py +++ b/openasip/scheduler/testbench/scheduler_tester.py @@ -1238,7 +1238,7 @@ def printLatexHeader(self, firstColumnWidth=30, valueColumnWidth=16): for i in range(0, len(moreStats)*len(self.archs)): cols += 'l|' - sys.stdout.write('\\begin{tabular}{|l|%s} \hline\n' % cols) + sys.stdout.write('\\begin{tabular}{|l|%s} \\hline\n' % cols) sys.stdout.write(''.ljust(firstColumnWidth)) sys.stdout.write(' & ') archsPrinted = 0 @@ -1272,14 +1272,14 @@ def printLatexHeader(self, firstColumnWidth=30, valueColumnWidth=16): if archsPrinted < len(self.archs): sys.stdout.write(' &') - sys.stdout.write('\\\\ \hline\n') + sys.stdout.write('\\\\ \\hline\n') def printLatexRow(self, testCase, firstColumnWidth=30): """ Prints a single row of the LaTeX table. """ global moreStats - sys.stdout.write(os.path.basename(testCase.directory).replace('_', '\_').ljust(firstColumnWidth)) + sys.stdout.write(os.path.basename(testCase.directory).replace('_', '\\_').ljust(firstColumnWidth)) sys.stdout.write(' & ') archsPrinted = 0 diff --git a/openasip/src/applibs/HWGen/CoproCusops.hh b/openasip/src/applibs/HWGen/CoproCusops.hh new file mode 100644 index 000000000..429976ea1 --- /dev/null +++ b/openasip/src/applibs/HWGen/CoproCusops.hh @@ -0,0 +1,75 @@ +/* + Copyright (C) 2025 Tampere University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ +/** + * @file CoproCusops.hh + * + * Custom Instruction encoding assigning + * @author Tharaka Sampath + */ + +#ifndef COPRO_CUSOPS_HH +#define COPRO_CUSOPS_HH + +#include + +#include "BEMGenerator.hh" +#include "BinaryEncoding.hh" +#include "InstructionFormat.hh" +#include "Machine.hh" +#include "MapTools.hh" +#include "RISCVFields.hh" +#include "RISCVTools.hh" + +namespace TTAMachine { +class Machine; +} + +class BinaryEncoding; +class InstructionFormat; + +class CoproCusops { +public: + CoproCusops(const TTAMachine::Machine& machine, bool roccEn) { + bem_ = BEMGenerator(machine, roccEn).generate(); + RISCVTools::findCustomOps(Ops_, bem_); + } + + // Making the Custom RISCV full instruction encoding + std::string + cusencode(std::string operation) { + std::string encode = " Not found"; + std::string reg = "00000"; + + for (auto op : Ops_) { + if (op.first == operation) { + encode = + RISCVTools::getFunc7Str(op.second).erase(0, 2) + reg + + reg + RISCVTools::getFunc3Str(op.second).erase(0, 2) + + reg + RISCVTools::getOpcodeStr(op.second).erase(0, 2); + } + } + return encode; + } + +private: + std::map Ops_; + BinaryEncoding* bem_; +}; + +#endif diff --git a/openasip/src/applibs/HWGen/FUGen.cc b/openasip/src/applibs/HWGen/FUGen.cc index 1920f0626..7ff9961ba 100644 --- a/openasip/src/applibs/HWGen/FUGen.cc +++ b/openasip/src/applibs/HWGen/FUGen.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2017-2019 Tampere University. + Copyright (c) 2017-2025 Tampere University. This file is part of TTA-Based Codesign Environment (TCE). @@ -27,33 +27,37 @@ * @author Lasse Lehtonen 2017 (lasse.lehtonen-no.spam-tut.fi) * @author Kati Tervo 2017-2019 (kati.tervo-no.spam-tuni.fi) * @author Topi Leppänen 2019 (topi.leppanen-no.spam-tuni.fi) + * @author Tharaka Sampath 2024 (Coprocessor modifications) */ -#include -#include "IPXact.hh" #include "FUGen.hh" -#include "ProGeTools.hh" -#include "ContainerTools.hh" + +#include + +#include "BinaryOps.hh" #include "ConstantNode.hh" +#include "ContainerTools.hh" +#include "CoproCusops.hh" #include "FUPort.hh" #include "FunctionUnit.hh" #include "HDBManager.hh" #include "HWOperation.hh" +#include "IPXact.hh" +#include "MemoryBusInterface.hh" #include "NetlistPort.hh" +#include "NetlistPortGroup.hh" #include "Operand.hh" #include "Operation.hh" #include "OperationIndex.hh" #include "OperationNode.hh" +#include "OperationPimpl.hh" #include "OperationPool.hh" +#include "ProGeTools.hh" #include "ProGeTypes.hh" -#include "TerminalNode.hh" -#include "WidthTransformations.hh" -#include "BinaryOps.hh" -#include "OperationPimpl.hh" #include "Signal.hh" #include "SignalGroupTypes.hh" -#include "NetlistPortGroup.hh" -#include "MemoryBusInterface.hh" +#include "TerminalNode.hh" +#include "WidthTransformations.hh" using namespace HDLGenerator; @@ -120,13 +124,29 @@ FUGen::opcodeSignal(int stage) { std::string FUGen::triggerSignal(int stage) { - if (stage == 0) { + if ((generateCVXIF_) || (generateROCC_)) { + return "operation_enable_in"; + } else if (stage == 0) { return "load_" + triggerPort_ + "_in"; } else { return "optrig_" + std::to_string(stage) + "_r"; } } +std::string +FUGen::enableSignal(std::string name, int cycle) { + if (!ContainerTools::containsValue(enablesignals_, name + "_enable")) + enablesignals_.emplace_back(name + "_enable"); + + if (cycle == 0) { + addWireIfMissing(name + "_enable"); + return name + "_enable"; + } else { + addWireIfMissing(name + "_enable_" + std::to_string(cycle)); + return name + "_enable_" + std::to_string(cycle); + } +} + std::string FUGen::opcodeConstant(std::string operation) { return "op_" + operation + "_c"; @@ -151,6 +171,26 @@ FUGen::pipelineName(std::string port, int cycle) { } } +// Pipeline config namings +std::string +FUGen::pipelineConfig(std::string port, int cycle) { + if (cycle == 0) { + return "configbits_" + port; + } else { + return "configbits_" + port + "_" + std::to_string(cycle) + "_r"; + } +} + +// pipeline shadowed operand name pullup +std::string +FUGen::pipelineNameShadow(std::string port, int cycle) { + if (cycle == 0) { + return "data_shadow" + port; + } else { + return "data_shadow" + port + "_" + std::to_string(cycle) + "_r"; + } +} + std::string FUGen::pipelineValid(std::string port, int cycle) { return "data_" + port + "_" + std::to_string(cycle) + "_valid_r"; @@ -225,11 +265,14 @@ FUGen::DAGNodeOperandWidth( * Creates the header comment for fu. */ void -FUGen::createFUHeaderComment() { +FUGen::createFUHeaderComment(const TTAMachine::Machine& machine) { fu_.appendToHeader("Function Unit: " + fug_.name()); fu_.appendToHeader(""); fu_.appendToHeader("Operations:"); + CoproCusops cusopgen = + CoproCusops(machine, generateROCC_); // For coprocessor encodings + if (adfFU_->operationCount() > 1) { size_t maxOpNameLen = 0; for (int i = 0; i < adfFU_->operationCount(); ++i) { @@ -245,7 +288,14 @@ FUGen::createFUHeaderComment() { std::sort(operations_.begin(), operations_.end()); int opcode = 0; for (auto&& op : operations_) { - fu_ << BinaryConstant(opcodeConstant(op), opcodeWidth_, opcode); + if ((generateCVXIF_) || (generateROCC_)) { + fu_ << BinaryConstant( + opcodeConstant(op), opcodeWidth_, opcode, + cusopgen.cusencode(op)); + } else { + fu_ << BinaryConstant( + opcodeConstant(op), opcodeWidth_, opcode); + } std::ostringstream comment; comment << boost::format( " %-" + std::to_string(maxOpNameLen) + "s : %" + @@ -259,6 +309,13 @@ FUGen::createFUHeaderComment() { TTAMachine::HWOperation* hwop = adfFU_->operation(0); operations_.emplace_back(hwop->name()); fu_.appendToHeader(" " + hwop->name() + " : 0"); + + if ((generateCVXIF_) || (generateROCC_)) { + int opcode = 1; + fu_ << BinaryConstant( + opcodeConstant(hwop->name()), opcodeWidth_, opcode, + cusopgen.cusencode(hwop->name())); + } } fu_.appendToHeader(""); } @@ -300,6 +357,12 @@ FUGen::createImplementationFiles() { Path file = dir / (fu_.name() + ".v"); std::ofstream ofs(file); fu_.implement(ofs, Language::Verilog); + } else if (options_.language == ProGe::HDL::SV) { // SV + Path dir = Path(options_.outputDirectory) / "systemverilog"; + FileSystem::createDirectory(dir.string()); + Path file = dir / (fu_.name() + ".sv"); + std::ofstream ofs(file); + fu_.implement(ofs, Language::Verilog); } // Copy synthesis files and simulation models. @@ -331,12 +394,50 @@ FUGen::createMandatoryPorts() { } else { resetPort = "rst"; } + fu_ << InPort("clk") << InPort(resetPort); + // For CVXIF coprocessor FU's + if (generateCVXIF_) { + fu_ << InPort( + "configbits_in", Nconfigbits_, + WireType::Vector); // configbits input + fu_ << InPort( + "operation_in", opcodeWidth_, + WireType::Vector); // For instruction input + fu_ << InPort("operation_enable_in"); + fu_ << InPort("result_ready_in"); + for (auto op : operations_) { + fu_ << InPort("config_" + op + "_enable_in"); + fu_ << InPort("config_" + op + "_kill_in"); + } + fu_ << OutPort("accept_o"); // Accept output + fu_ << OutPort("ready_o"); // Ready for accepting instructions + fu_ << OutPort( + "configbits_out", Nconfigbits_, + WireType::Vector); // configbits output + for (auto op : + operations_) { // configbits outputs for each operation + fu_ << OutPort( + "configbits_" + op + "_out", Nconfigbits_, WireType::Vector); + } + } else if (generateROCC_) { // For ROCC interface connections + fu_ << InPort("glock_in") << InPort("out_ready") + << InPort("operation_in", opcodeWidth_, WireType::Vector) + << InPort("configs_in", 6, WireType::Vector) + << InPort("operation_enable_in"); - fu_ << InPort("clk") << InPort(resetPort) << InPort("glock_in") - << OutPort("glockreq_out"); + fu_ << OutPort("out_valid") + << OutPort( + "configs_out", 5, + WireType::Vector); // Configs has 5 bits + enable bit - if (adfFU_->operationCount() > 1) { - fu_ << InPort("operation_in", opcodeWidth_, WireType::Vector); + } else { + fu_ << InPort("glock_in") << OutPort("glockreq_out"); + if (adfFU_->operationCount() > 1) { + fu_ << InPort("operation_in", opcodeWidth_, WireType::Vector); + } + if (addressWidth_ > 0) { + fu_ << IntegerConstant("addrw_c", addressWidth_); + } } // operand ports. @@ -348,17 +449,17 @@ FUGen::createMandatoryPorts() { fu_ << InPort( "data_" + adfPort->name() + "_in", adfPort->width(), WireType::Vector); - fu_ << InPort("load_" + adfPort->name() + "_in"); + if ((generateCVXIF_) || (generateROCC_)) { + NULL; + } else { + fu_ << InPort("load_" + adfPort->name() + "_in"); + } } else { fu_ << OutPort( "data_" + adfPort->name() + "_out", adfPort->width(), WireType::Vector); } } - - if (addressWidth_ > 0) { - fu_ << IntegerConstant("addrw_c", addressWidth_); - } } void @@ -370,22 +471,38 @@ FUGen::checkForValidity() { TTAMachine::HWOperation* hwOp = adfFU_->operation(op); int hwOpOperands = hwOp->operandCount(); int prevLatency = -1; + int numinputs = 0; + int numoutputs = 0; for (int operand = 0; operand < hwOpOperands; ++operand) { TTAMachine::FUPort* fuPort = hwOp->port(operand + 1); - if (!fuPort->isOutput()) { - continue; - } - - int latency = hwOp->latency(operand + 1); - if (prevLatency == -1) { - prevLatency = latency; - } else if (prevLatency != latency) { - // TODO: probably not true anymore, but needs to be tested - throw std::runtime_error( - "FUGen cannot implement multioutput operations (" + op + - ") which have different latencies for" - " its outputs."); + // CVXIF coprocessor features selection + if (generateCVXIF_) { + if (fuPort->isOutput()) { + numoutputs++; + if (numoutputs == 1) { + continue; + } else { + throw std::runtime_error( + "FU should have only one output but there are " + "more than one output"); + } + } + } else { + if (!fuPort->isOutput()) { + continue; + } + int latency = hwOp->latency(operand + 1); + if (prevLatency == -1) { + prevLatency = latency; + } else if (prevLatency != latency) { + // TODO: probably not true anymore, but needs to be tested + throw std::runtime_error( + "FUGen cannot implement multioutput operations (" + + op + + ") which have different latencies for" + " its outputs."); + } } } } @@ -735,7 +852,7 @@ FUGen::buildOperations() { } if (!operand.isOutput) { operationCp.reads(destination); - } + } } replacesPerOp_[name] = buildReplaces(name); @@ -805,17 +922,86 @@ FUGen::buildOperations() { continue; } - if (operations_.size() > 1) { - Case opCase(opcodeConstant(op)); - opCase << onTrigger; - opSwitch.addCase(opCase); - emptySwitch = false; + if (generateCVXIF_) { // Adding the if else clause for the Opcode + // selections with the accept signal + int Nstage; + for (int i = 0; i < adfFU_->portCount(); ++i) { + TTAMachine::FUPort* adfPort = + static_cast(adfFU_->port(i)); + if (adfPort->isOutput()) { + auto inputs = + portInputs_.equal_range(adfPort->name()); + for (auto it = inputs.first; it != inputs.second; + ++it) { + auto connection = it->second; + if (connection.operation == op) { + Nstage = + connection + .pipelineStage; // extracting the + // pipeline stage for + // the operation + } + } + } + } + Equals accepted( + LHSSignal(enableSignal(op, Nstage)), BinaryLiteral("'1")); + onTrigger.append(Assign("accept_o", BinaryLiteral("'1"))); + onTrigger.append(Assign("ready_o", BinaryLiteral("'1"))); + onTrigger.append( + Assign("configbits_" + op, LHSSignal("configbits_in"))); + fu_ << Wire("configbits_" + op, Nconfigbits_); + If ifstatement(accepted, onTrigger); + ifstatement.elseClause( + Assign("ready_o", BinaryLiteral("'0"))); + // Adding default values + defaultValues.append( + Assign("configbits_" + op, BinaryLiteral("0"))); + if (operations_.size() > 1) { + Case opCase(opcodeConstant(op)); + opCase << ifstatement; + opSwitch.addCase(opCase); + emptySwitch = false; + } else { + If opIf( + LogicalAnd( + Equals( + LHSSignal("operation_enable_in"), + BinaryLiteral("'1")), + Equals( + LHSSignal("operation_in"), + BinaryLiteral(opcodeConstant(op)))), + ifstatement); + triggeredSnippets.append(opIf); + } } else { - If opIf( - Equals( - LHSSignal(triggerSignal(cycle)), BinaryLiteral("1")), - onTrigger); - triggeredSnippets.append(opIf); + if (operations_.size() > 1) { + Case opCase(opcodeConstant(op)); + opCase << onTrigger; + opSwitch.addCase(opCase); + emptySwitch = false; + } else { // When there is only one operation in the FU + If opIf( + BinaryLiteral("1"), DefaultAssign("dummy")); // Dummy + if (generateROCC_) { // For ROCC + opIf = + If(LogicalAnd( + Equals( + LHSSignal("operation_enable_in"), + BinaryLiteral("'1")), + Equals( + LHSSignal("operation_in"), + BinaryLiteral(opcodeConstant(op)))), + onTrigger); + } else { // For TTA + opIf = + If(Equals( + LHSSignal(triggerSignal(cycle)), + BinaryLiteral("1")), + onTrigger); + } + triggeredSnippets.append(opIf); + } } } @@ -827,7 +1013,12 @@ FUGen::buildOperations() { triggeredSnippets.append(opIf); } } - + if (generateCVXIF_) { // Adding accept and ready conditions for CV-X-If + // control + // logic + defaultValues.append(Assign("accept_o", BinaryLiteral("0"))); + defaultValues.append(Assign("ready_o", BinaryLiteral("'1"))); + } if (useGlock_) { operationCp.reads("glock_in"); } @@ -862,8 +1053,8 @@ FUGen::buildOperations() { } } - operationCp << defaultValues << defaultSnippets - << triggeredSnippets << operationOutAssignments; + operationCp << defaultValues << defaultSnippets << triggeredSnippets + << operationOutAssignments; behaviour_ << operationCp; } @@ -904,12 +1095,19 @@ FUGen::prepareSnippet( void FUGen::finalizeHDL() { - // Create lock request wires - if (useGlockRequest_) { - fu_ << Wire("glockreq"); - behaviour_ << Assign("glockreq_out", LHSSignal("glockreq")); + if (generateCVXIF_) { + fu_.setPackages("cvxif_sup_pkg"); // Adding the package which has the + // configbit lengths + } else if (generateROCC_) { + NULL; } else { - behaviour_ << Assign("glockreq_out", BinaryLiteral("0"), true); + // Create lock request wires + if (useGlockRequest_) { + fu_ << Wire("glockreq"); + behaviour_ << Assign("glockreq_out", LHSSignal("glockreq")); + } else { + behaviour_ << Assign("glockreq_out", BinaryLiteral("0"), true); + } } // Finalize and set global options. @@ -1256,6 +1454,73 @@ FUGen::scheduleOperations() { } } +// Making inputs connected with the operand inputs +void +FUGen::CreateInputsConnected() { + std::unordered_map currentName; + std::unordered_map portWidth; + CodeBlock inregisterblock; + + for (int i = 0; i < adfFU_->portCount(); ++i) { + TTAMachine::FUPort* adfPort = + static_cast(adfFU_->port(i)); + portWidth[adfPort->name()] = adfPort->width(); + + if (adfPort->isInput()) { + currentName[adfPort->name()] = "data_" + adfPort->name() + "_in"; + std::string operationOperands = "data_" + adfPort->name(); + + addWireIfMissing( + operationOperands, portWidth[adfPort->name()], + WireType::Vector); + inregisterblock.append(Assign( + operationOperands, LHSSignal(currentName[adfPort->name()]))); + } + } + behaviour_ << (Asynchronous("Connecting input ports") << inregisterblock); +} + +// Making all the inputs registered +void +FUGen::CreateInputRegister() { + std::unordered_map currentName; + std::unordered_map portWidth; + CodeBlock inregisterblock; + + for (int i = 0; i < adfFU_->portCount(); ++i) { + TTAMachine::FUPort* adfPort = + static_cast(adfFU_->port(i)); + portWidth[adfPort->name()] = adfPort->width(); + + if (adfPort->isInput()) { + currentName[adfPort->name()] = "data_" + adfPort->name() + "_in"; + std::string RegisteredInput = "data_" + adfPort->name() + "_r"; + + addRegisterIfMissing( + RegisteredInput, portWidth[adfPort->name()], + WireType::Vector); + inregisterblock.append(Assign( + RegisteredInput, LHSSignal(currentName[adfPort->name()]))); + } + } + // configbits_in port registering + std::string Configport = "configbits_r"; + std::string ConfigportIn = "configbits_in"; + addRegisterIfMissing(Configport, Nconfigbits_, WireType::Vector); + inregisterblock.append(Assign(Configport, LHSSignal(ConfigportIn))); + + // operation port registering + std::string registeredOp = "operation_in_r"; + std::string opportIn = "operation_in"; + addRegisterIfMissing(registeredOp, opcodeWidth_, WireType::Vector); + inregisterblock.append(Assign(registeredOp, LHSSignal(opportIn))); + + std::string InputEnPort = enableSignal("input", 0); + If InputEnregister( + Equals(LHSSignal(InputEnPort), BinaryLiteral("1")), inregisterblock); + behaviour_ << (Synchronous("InputRegistering_sp") << InputEnregister); +} + void FUGen::createShadowRegisters() { std::vector inOperands; @@ -1279,8 +1544,13 @@ FUGen::createShadowRegisters() { if (!adfPort->noRegister()) { registeredInOperands.emplace(adfPort->name()); } - - std::string name = pipelineName(adfPort->name(), 0); + std::string name; + // CVXIF selection + if (generateCVXIF_) { + name = pipelineNameShadow(adfPort->name(), 0); + } else { + name = pipelineName(adfPort->name(), 0); + } fu_ << Wire(name, adfPort->width()); } else { currentName[adfPort->name()] = "data_" + adfPort->name() + "_out"; @@ -1300,7 +1570,13 @@ FUGen::createShadowRegisters() { for (auto&& p : inOperands) { std::string dataPort = currentName[p]; - std::string output = pipelineName(p, 0); + std::string output; + // CVXIF selection + if (generateCVXIF_) { + output = pipelineNameShadow(p, 0); + } else { + output = pipelineName(p, 0); + } if (p != triggerPort_ && registeredInOperands.find(p) != registeredInOperands.end()) { std::string loadPort = "load_" + p + "_in"; @@ -1308,13 +1584,19 @@ FUGen::createShadowRegisters() { fu_ << Register(shadowReg, portWidth[p], ResetOption::Optional); - auto noLock = Equals(LHSSignal("glock_in"), BinaryLiteral("0")); auto portLoad = Equals(LHSSignal(loadPort), BinaryLiteral("1")); - If iffi( - noLock && portLoad, Assign(shadowReg, LHSSignal(dataPort))); - - behaviour_ << (Synchronous("shadow_" + p + "_sp") << iffi); - + // CVXIF selection + if (generateCVXIF_) { + If iffi(portLoad, Assign(shadowReg, LHSSignal(dataPort))); + behaviour_ << (Synchronous("shadow_" + p + "_sp") << iffi); + } else { + auto noLock = + Equals(LHSSignal("glock_in"), BinaryLiteral("0")); + If iffi( + noLock && portLoad, + Assign(shadowReg, LHSSignal(dataPort))); + behaviour_ << (Synchronous("shadow_" + p + "_sp") << iffi); + } auto triggerLoad = Equals( LHSSignal("load_" + triggerPort_ + "_in"), BinaryLiteral("1")); @@ -1358,23 +1640,52 @@ FUGen::createPortPipeline() { } // Pipelines for operand data - for (int i = 0; i < adfFU_->portCount(); ++i) { - auto port = adfFU_->port(i); - int length = pipelineLength_[port->name()]; - int width = port->width(); - if (portDirection_[port->name()] != ProGe::Direction::IN) { - continue; + if (generateCVXIF_) { + for (int i = 0; i < adfFU_->portCount(); ++i) { + auto port = adfFU_->port(i); + int length = maxLatency_; + int width = port->width(); + if (portDirection_[port->name()] != ProGe::Direction::IN) { + continue; + } + + for (int i = 0; i < length; ++i) { + std::string prevReg = pipelineName(port->name(), i); + std::string nextReg = pipelineName(port->name(), i + 1); + addRegisterIfMissing(nextReg, width, WireType::Vector); + if (i == -1) { + firstStage.append(Assign(nextReg, LHSSignal(prevReg))); + } else { + pipelineAssignments.append( + Assign(nextReg, LHSSignal(prevReg))); + } + } } - for (int i = 0; i < length; ++i) { - std::string prevReg = pipelineName(port->name(), i); - std::string nextReg = pipelineName(port->name(), i + 1); - addRegisterIfMissing(nextReg, width, WireType::Vector); - if (i == 0) { - firstStage.append(Assign(nextReg, LHSSignal(prevReg))); - } else { - pipelineAssignments.append( - Assign(nextReg, LHSSignal(prevReg))); + pipelineAssignments.append(firstStage); + Synchronous pipeline("input_pipeline_sp"); + pipeline << pipelineAssignments; + behaviour_ << pipeline; + + } else { + for (int i = 0; i < adfFU_->portCount(); ++i) { + auto port = adfFU_->port(i); + int length = pipelineLength_[port->name()]; + int width = port->width(); + if (portDirection_[port->name()] != ProGe::Direction::IN) { + continue; + } + + for (int i = 0; i < length; ++i) { + std::string prevReg = pipelineName(port->name(), i); + std::string nextReg = pipelineName(port->name(), i + 1); + addRegisterIfMissing(nextReg, width, WireType::Vector); + if (i == 0) { + firstStage.append(Assign(nextReg, LHSSignal(prevReg))); + } else { + pipelineAssignments.append( + Assign(nextReg, LHSSignal(prevReg))); + } } } } @@ -1397,12 +1708,29 @@ FUGen::addRegisterIfMissing(std::string name, int width, WireType wt) { registers_.emplace_back(name); } } +// Adding register for string widths +void +FUGen::addRegisterIfMissing( + std::string name, std::string width, WireType wt) { + if (!ContainerTools::containsValue(registers_, name)) { + fu_ << Register(name, width, ResetOption::Optional); + registers_.emplace_back(name); + } +} +void +FUGen::addWireIfMissing(std::string name, int width, WireType wt) { + if (!ContainerTools::containsValue(wires_, name)) { + fu_ << Wire(name, width, wt); + wires_.emplace_back(name); + } +} void FUGen::createOutputPipeline() { CodeBlock outputPipeline; CodeBlock lastStage; - + CodeBlock outputElsebody; + for (int i = 0; i < adfFU_->portCount(); ++i) { auto port = adfFU_->port(i); int length = pipelineLength_[port->name()]; @@ -1418,6 +1746,19 @@ FUGen::createOutputPipeline() { std::string nextReg = pipelineName(port->name(), cycle); std::string prevReg = pipelineName(port->name(), cycle + 1); std::string valid = pipelineValid(port->name(), cycle); + std::string nextconfigReg; + std::string prevconfigReg; + // For ROCC interface + if (generateROCC_) { + nextconfigReg = pipelineConfig(port->name(), cycle); + prevconfigReg = pipelineConfig(port->name(), cycle + 1); + if (cycle == 0) { + fu_ << Wire(nextconfigReg, width, WireType::Vector); + } else { + addRegisterIfMissing( + nextconfigReg, width, WireType::Vector); + } + } if (cycle == 0) { fu_ << Wire(nextReg, width, WireType::Vector); @@ -1448,20 +1789,24 @@ FUGen::createOutputPipeline() { operandSignal(connection.operation, connection.operandID), width, connection.operandWidth); + CodeBlock regAssignBlk; + regAssignBlk.append(Assign(nextReg, source)); + if (generateROCC_) { + regAssignBlk.append( + Assign(nextconfigReg, LHSSignal("configs_in"))); + } + if (cycleActive) { if (operations_.size() == 1) { - validOperations.elseIfClause( - triggered, Assign(nextReg, source)); + validOperations.elseIfClause(triggered, regAssignBlk); } else { - validOperations.elseIfClause( - active, Assign(nextReg, source)); + validOperations.elseIfClause(active, regAssignBlk); } } else { if (operations_.size() == 1) { - validOperations = - If(triggered, Assign(nextReg, source)); + validOperations = If(triggered, regAssignBlk); } else { - validOperations = If(active, Assign(nextReg, source)); + validOperations = If(active, regAssignBlk); } } cycleActive = true; @@ -1474,16 +1819,22 @@ FUGen::createOutputPipeline() { pipelineValid(port->name(), cycle + 1); Equals isValid(LHSSignal(prevValid), BinaryLiteral("1")); + + CodeBlock interAssignBlk; + interAssignBlk.append(Assign(nextReg, LHSSignal(prevReg))); + if (generateROCC_) { + interAssignBlk.append( + Assign(nextconfigReg, LHSSignal(prevconfigReg))); + } + if (cycleActive) { - validOperations.elseIfClause( - isValid, Assign(nextReg, LHSSignal(prevReg))); + validOperations.elseIfClause(isValid, interAssignBlk); } else { if (cycle == 0) { skip_last_assign = true; - lastStage.append(Assign(nextReg, LHSSignal(prevReg))); + lastStage.append(interAssignBlk); } else { - validOperations = - If(isValid, Assign(nextReg, LHSSignal(prevReg))); + validOperations = If(isValid, interAssignBlk); } } cycleActive = true; @@ -1504,13 +1855,28 @@ FUGen::createOutputPipeline() { } } } else { - validOperations.elseClause(Assign(valid, BinaryLiteral("0"))); + CodeBlock elseBody; + elseBody.append(Assign(valid, BinaryLiteral("0"))); + // For ROCC, making output zero explicitly + if ((cycle == 1) && (generateROCC_)) { + elseBody.append(Assign(nextReg, BinaryLiteral("0"))); + elseBody.append( + Assign(nextconfigReg, BinaryLiteral("0"))); + // CP config assignment to Config_out and Out_valid + lastStage.append(Assign( + "configs_out", LHSSignal(nextconfigReg + "[5:1]"))); + lastStage.append(Assign( + "out_valid", LHSSignal(nextconfigReg + "[0]"))); + } + validOperations.elseClause(elseBody); outputPipeline.append(validOperations); } } lastStage.append(Assign( "data_" + port->name() + "_out", LHSSignal(pipelineName(port->name(), 0)))); + outputElsebody.append( + Assign("data_" + port->name() + "_out", BinaryLiteral("0"))); } Synchronous sync("output_pipeline_sp"); @@ -1519,10 +1885,308 @@ FUGen::createOutputPipeline() { behaviour_ << sync; Asynchronous async("output_pipeline_cp"); - async << lastStage; + if (generateROCC_) { // For ROCC, pipeline outputs are written only when + // the + // processor has raised the out_ready + If outputAssign( + Equals(LHSSignal("out_ready"), BinaryLiteral("1")), lastStage); + // setting pipeline select else values + outputElsebody.append(Assign("configs_out", BinaryLiteral("0"))); + outputElsebody.append(Assign("out_valid", BinaryLiteral("0"))); + outputAssign.elseClause(outputElsebody); + async << outputAssign; + } else { + async << lastStage; + } behaviour_ << async; } +// Output pipeline for CV-X-IF FU +void +FUGen::createOutputPipelineCVXIF() { + CodeBlock operationPipeline; + CodeBlock cotrolLogic_comb; + CodeBlock DefaultVals; + for (std::string op : operations_) { + int Nstage; + int width; + FUGen::OutputConnection connected; + for (int i = 0; i < adfFU_->portCount(); ++i) { + TTAMachine::FUPort* adfPort = + static_cast(adfFU_->port(i)); + width = adfPort->width(); + if (adfPort->isOutput()) { + auto inputs = portInputs_.equal_range(adfPort->name()); + for (auto it = inputs.first; it != inputs.second; ++it) { + auto connection = it->second; + if (connection.operation == op) { + Nstage = + connection + .pipelineStage; // Extracting the pipeline + // stage for the operation + connected = connection; + } + } + } + } + CodeBlock stage_block; + CodeBlock ctrl_block; + CodeBlock ctrlLogicBlk; + CodeBlock ctrlSignals; + Equals cntrlCondition(LHSSignal("Dum_sig"), LHSSignal("Dum_sig")); + for (int pipestage = Nstage; pipestage > 0; pipestage--) { + std::string nextReg = pipelineName(op, pipestage); + std::string prevReg = pipelineName(op, pipestage + 1); + std::string nextConfigbits = pipelineConfig(op, pipestage); + std::string prevConfigbits = pipelineConfig(op, pipestage + 1); + std::string ifEnablesignal = enableSignal(op, pipestage - 1); + std::string setEnablesignal = enableSignal(op, pipestage); + CodeBlock out_configs; + Equals condition(LHSSignal("Dum_sig"), LHSSignal("Dum_sig")); + + // Adding the register signals + addRegisterIfMissing(nextReg, width, WireType::Vector); + addRegisterIfMissing( + nextConfigbits, Nconfigbits_, WireType::Vector); + + // Pipeline control signal assignment + ctrlSignals.append(Assign(setEnablesignal, BinaryLiteral("'1"))); + DefaultVals.append(Assign(setEnablesignal, BinaryLiteral("'0"))); + addWireIfMissing(setEnablesignal, 1); + + std::string selConfigbit = + nextConfigbits + "[cvxif_sup_pkg::NConfigbits_C-1]"; + std::string configEnBit = ""; + if (pipestage == 1) { + configEnBit = "config_" + op + "_kill_in"; + // Pipeline control enable signals + condition = Equals( + BitwiseOr( + BitwiseOr( + LHSSignal(ifEnablesignal), + LHSSignal(configEnBit)), + BitwiseNot(LHSSignal(selConfigbit))), + BinaryLiteral("'1")); + } else { + condition = Equals( + BitwiseOr(BitwiseOr( + LHSSignal(ifEnablesignal), + BitwiseNot(LHSSignal(selConfigbit)))), + BinaryLiteral("'1")); + } + + if (pipestage == + Nstage) { // Assigning Operation outputs at the begining + Ext source( + operandSignal(connected.operation, connected.operandID), + width, connected.operandWidth); + out_configs.append(Assign(nextReg, source)); + out_configs.append( + Assign(nextConfigbits, LHSSignal("configbits_" + op))); + } else { // Assigning rest of the pipelining stages + out_configs.append(Assign(nextReg, LHSSignal(prevReg))); + out_configs.append( + Assign(nextConfigbits, LHSSignal(prevConfigbits))); + } + + If ifblock(condition, out_configs); + stage_block.append(ifblock); + // Pipeline control Logic if block + If ifcntrl(condition, ctrlSignals); + ctrl_block.append(ifcntrl); + } + + operationPipeline.append(stage_block); + cotrolLogic_comb.append(ctrl_block); + } + + Asynchronous async("Pipeline Control Logic"); + async << DefaultVals << cotrolLogic_comb; + behaviour_ << async; + + Synchronous sync("output_pipeline_sp"); + sync << operationPipeline; + behaviour_ << sync; +} + +// Outputs binary value with a bit width equal to "width", having 1 to the +// position of value and rest are 0(index starts) +std::string +FUGen::valtoBinaryOne(int width, int value) { + std::string binVal = ""; + for (int i = width; i > 0; --i) { + if (i == value) { + binVal += "1"; + } else { + binVal += "0"; + } + } + return std::to_string(width) + "'b" + binVal; +} + +// Selection signal logic generation for CV-X-IF +void +FUGen::selectionlogic() { + CodeBlock selBlock; + CodeBlock configSignalBlck; + CodeBlock DefaultVal; + std::string configsignal = "{"; + for (std::string op : + operations_) { // Making the config selection signal + configsignal = configsignal + "config_" + op + "_enable_in, "; + configSignalBlck.append(Assign( + "configbits_" + op + "_out", + LHSSignal("configbits_" + op + "_1_r"))); + } + configsignal = configsignal.substr(0, configsignal.size() - 2); + configsignal = configsignal + "}"; + configSignalBlck.append(Assign("config_sel", LHSSignal(configsignal))); + fu_ << Wire("config_sel", operations_.size()); + + Switch selSwitch( + LHSSignal("prev_sel_r")); // Selection shifting case block + for (int i = 1; i < operations_.size() + 1; i++) { + CodeBlock individualCaseBlock; + std::string caseString = valtoBinaryOne(operations_.size(), i); + fu_ << Wire("prev_shifted" + std::to_string(i), operations_.size()); + + if (i == operations_.size()) { // Default case + caseString = "default"; + for (int j = 1; j < operations_.size() + 1; j++) { + individualCaseBlock.append(Assign( + "prev_shifted" + std::to_string(j), + LHSSignal(valtoBinaryOne(operations_.size(), j)))); + } + } else { + int val = i; + for (int j = 1; j < operations_.size() + 1; j++) { + int newVal = val + j; + individualCaseBlock.append(Assign( + "prev_shifted" + std::to_string(j), + LHSSignal(valtoBinaryOne(operations_.size(), newVal)))); + if (newVal == operations_.size()) { + val = -j; + } + } + } + Case selCase(caseString); + selCase << individualCaseBlock; + selSwitch.addCase(selCase); + } + selBlock.append(selSwitch); + + CodeBlock newselSignal; // Seperately, makes the New selection logic + If ifBlock(BinaryLiteral("1"), DefaultAssign("dummy")); + for (int i = 1; i < operations_.size() + 1; i++) { + HDLGenerator::LHSValue andedsignals; + HDLGenerator::LHSValue oredsignalsR; + for (int j = 0; j < operations_.size(); j++) { + BitwiseAnd Anded( + LHSSignal( + "prev_shifted" + std::to_string(i) + "[" + + std::to_string(j) + "]"), + LHSSignal("config_sel[" + std::to_string(j) + "]")); + if (j == 0) { + andedsignals = Anded; + } else { + BitwiseOr oredsignals(Anded, andedsignals); + andedsignals = oredsignals; + oredsignalsR = oredsignals; + } + } + if (operations_.size() == 1) { + oredsignalsR = LHSSignal("(config_sel)"); + } + if (i == 1) { + ifBlock = + If(oredsignalsR, + Assign( + "new_sel_d", + LHSSignal("prev_shifted" + std::to_string(i)))); + } else { + ifBlock.elseIfClause( + oredsignalsR, + Assign( + "new_sel_d", + LHSSignal("prev_shifted" + std::to_string(i)))); + } + } + newselSignal.append(ifBlock); + DefaultVal.append(Assign("new_sel_d", BinaryLiteral("'0"))); + + Asynchronous selSignalLogic("Selection signal logic"); + selSignalLogic << DefaultVal << configSignalBlck << selBlock + << newselSignal; + behaviour_ << selSignalLogic; +} + +// Selecting the pipeline to be set as the output for CV-X-IF +void +FUGen::outputSelect() { + CodeBlock selectedSignalsBlk; + CodeBlock DefaultVal; + // Output port Name extraction + std::string outputPortName = ""; + int j = 0; + for (int i = 0; i < adfFU_->portCount(); ++i) { + auto adfPort = adfFU_->port(i); + if (adfPort->isOutput()) { + j++; + outputPortName = adfPort->name(); + } + if (j == 2) { // Only one output port should be there on the adf for + // the Fu. + throw std::runtime_error( + "CVXIF FuGen must have only 1 output, But there are more " + "than 1 OUTPUT ports."); + } + } + + int k = operations_.size(); + Switch newSelectSwitch( + LHSSignal("new_sel_d")); // New selection signal case + std::string caseString = ""; + for (auto op : operations_) { + CodeBlock newselectedSignals; + caseString = valtoBinaryOne(operations_.size(), k); + + newselectedSignals.append( + Assign("configbits_out", LHSSignal(pipelineConfig(op, 1)))); + newselectedSignals.append( + Assign(enableSignal(op, 0), LHSSignal("result_ready_in"))); + newselectedSignals.append(Assign( + "data_" + outputPortName + "_out ", + LHSSignal(pipelineName(op, 1)))); + + DefaultVal.append(Assign( + enableSignal(op, 0), + BinaryLiteral("'0"))); // Default value assignment + + k--; + Case newSelect(caseString); + newSelect << newselectedSignals; + newSelectSwitch.addCase(newSelect); + } + selectedSignalsBlk.append(newSelectSwitch); + // Default value assignment + DefaultVal.append(Assign("configbits_out", BinaryLiteral("'0"))); + DefaultVal.append( + Assign("data_" + outputPortName + "_out ", BinaryLiteral("'0"))); + + CodeBlock selRegistring; + fu_ << Wire("new_sel_d", operations_.size()); + addRegisterIfMissing("prev_sel_r", operations_.size()); + selRegistring.append(Assign("prev_sel_r", LHSSignal("new_sel_d"))); + + Asynchronous newSelectLogic("Output Pipeline selection"); + newSelectLogic << DefaultVal << selectedSignalsBlk; + behaviour_ << newSelectLogic; + + Synchronous newSelectRegistring("Selection Signal registering"); + newSelectRegistring << selRegistring; + behaviour_ << newSelectRegistring; +} + /** * * Generate all FUGen FUs. @@ -1558,7 +2222,7 @@ FUGen::implement( fugen.backRegistered_ = true; } - fugen.createFUHeaderComment(); + fugen.createFUHeaderComment(machine); fugen.checkForValidity(); fugen.parseOperations(); @@ -1567,11 +2231,22 @@ FUGen::implement( fugen.createExternalInterfaces(!options.integratorName.empty()); fugen.createOperationResources(); - fugen.createShadowRegisters(); - fugen.createPortPipeline(); - fugen.buildOperations(); - fugen.createOutputPipeline(); - + if (fugen.generateCVXIF_) { // CVXIF selection + fugen.CreateInputsConnected(); + fugen.buildOperations(); + fugen.createOutputPipelineCVXIF(); + fugen.selectionlogic(); + fugen.outputSelect(); + } else if (fugen.generateROCC_) { // ROCC selection + fugen.CreateInputsConnected(); + fugen.buildOperations(); + fugen.createOutputPipeline(); + } else { // TTA selection + fugen.createShadowRegisters(); + fugen.createPortPipeline(); + fugen.buildOperations(); + fugen.createOutputPipeline(); + } fugen.finalizeHDL(); fugen.createImplementationFiles(); } diff --git a/openasip/src/applibs/HWGen/FUGen.hh b/openasip/src/applibs/HWGen/FUGen.hh old mode 100755 new mode 100644 index 09f8aad14..147cb0716 --- a/openasip/src/applibs/HWGen/FUGen.hh +++ b/openasip/src/applibs/HWGen/FUGen.hh @@ -1,5 +1,5 @@ /* - Copyright (c) 2017-2019 Tampere University. + Copyright (c) 2017-2025 Tampere University. This file is part of TTA-Based Codesign Environment (TCE). @@ -22,10 +22,10 @@ DEALINGS IN THE SOFTWARE. */ /** -* @file FUGen.hh -* -* @author Lasse Lehtonen 2017 (lasse.lehtonen-no.spam-tut.fi) -*/ + * @file FUGen.hh + * + * @author Lasse Lehtonen 2017 (lasse.lehtonen-no.spam-tut.fi) + */ #pragma once #include "FUGenerated.hh" @@ -86,6 +86,17 @@ public: auto as = adfFU_->addressSpace(); addressWidth_ = MathTools::requiredBits(as->end()); } + + // Checking the CVXIF coprocessor generation + if (options.CVXIFCoproGen) { + generateCVXIF_ = true; + generateROCC_ = false; + opcodeWidth_ = 32; + } else if (options.roccGen) { + generateCVXIF_ = false; + generateROCC_ = true; + opcodeWidth_ = 32; + } } static void implement(const ProGeOptions& options, @@ -142,14 +153,22 @@ private: }; void createOutputPipeline(); + // For creating output pipelines for CVXIF + void createOutputPipelineCVXIF(); void addRegisterIfMissing(std::string name, int width, HDLGenerator::WireType wt = HDLGenerator::WireType::Auto); + void addRegisterIfMissing( + std::string name, std::string width, + HDLGenerator::WireType wt = HDLGenerator::WireType::Auto); + void addWireIfMissing( + std::string name, int width = 1, + HDLGenerator::WireType wt = HDLGenerator::WireType::Auto); std::string findAbsolutePath(std::string file); - void createFUHeaderComment(); + void createFUHeaderComment(const TTAMachine::Machine& machine); void createMandatoryPorts(); void checkForValidity(); void createExternalInterfaces(bool genIntegrator); @@ -163,6 +182,11 @@ private: void scheduleOperations(); void createPortPipeline(); void createShadowRegisters(); + void CreateInputRegister(); // Make all the inputs registered + void CreateInputsConnected(); + void selectionlogic(); + void outputSelect(); + void assignvalues(); OperandConnection subOpConnection(OperationDAG* dag, OperationDAGEdge* edge, bool isOutput); @@ -183,6 +207,8 @@ private: HDLGenerator::Language selectedLanguage(); // Functions which construct pipelined signal names + std::string enableSignal( + std::string name, int cycle); // Enable signal pullup std::string opcodeSignal(int stage); std::string triggerSignal(int stage); std::string opcodeConstant(std::string operation); @@ -193,6 +219,11 @@ private: std::string subOpName(OperationNode* node); std::string constantName(ConstantNode* node, OperationDAG* dag); std::string constantName(DAGConstant dag); + // For pipeline name pullups + std::string registeredNameOP(std::string name); + std::string pipelineNameShadow(std::string port, int cycle); + std::string pipelineConfig(std::string port, int cycle); + std::string valtoBinaryOne(int width, int value); bool isLSUDataPort(const std::string& portName); ProGe::Signal inferLSUSignal(const std::string& portName) const; @@ -212,7 +243,6 @@ private: std::vector operations_; int opcodeWidth_; - std::string moduleName_; ProGe::NetlistBlock* netlistBlock_; @@ -250,6 +280,9 @@ private: std::string triggerPort_; std::vector registers_; + std::vector wires_; + std::vector enablesignals_; + std::string Nconfigbits_ = "cvxif_sup_pkg::NConfigbits_C"; bool useGlockRequest_ = false; bool useGlock_ = false; @@ -259,4 +292,8 @@ private: bool backRegistered_ = false; int addressWidth_ = 0; bool isLSU_ = false; + // CVXIF generator enable + bool generateCVXIF_ = false; + // ROCC enable + bool generateROCC_ = false; }; diff --git a/openasip/src/applibs/HWGen/HDLGenerator.hh b/openasip/src/applibs/HWGen/HDLGenerator.hh old mode 100755 new mode 100644 index 3cf3eaf90..31db919b7 --- a/openasip/src/applibs/HWGen/HDLGenerator.hh +++ b/openasip/src/applibs/HWGen/HDLGenerator.hh @@ -245,7 +245,18 @@ namespace HDLGenerator { class BinaryConstant : public Generatable { public: BinaryConstant(std::string name, int width, int value) - : Generatable(name), width_(width), value_(value) {} + : Generatable(name), + width_(width), + value_(value), + copro_(false) {} + + BinaryConstant( + std::string name, int width, int value, std::string encoding) + : Generatable(name), + width_(width), + value_(value), + copro_(true), + enconding_(encoding) {} int value() const noexcept { return value_; } @@ -254,13 +265,18 @@ namespace HDLGenerator { void declare(std::ostream& stream, Language lang, int level) { std::string binVal = ""; int tempVal = value_; - for (int i = width_ - 1; i >= 0; --i) { - long power = static_cast(std::pow(2, i)); - if (power <= tempVal) { - tempVal -= power; - binVal += "1"; - } else { - binVal += "0"; + + if (copro_) { + binVal = enconding_; + } else { + for (int i = width_ - 1; i >= 0; --i) { + long power = static_cast(std::pow(2, i)); + if (power <= tempVal) { + tempVal -= power; + binVal += "1"; + } else { + binVal += "0"; + } } } @@ -280,6 +296,9 @@ namespace HDLGenerator { private: int width_; int value_; + bool copro_; + std::string cusopcode_; + std::string enconding_; }; /** @@ -322,10 +341,11 @@ namespace HDLGenerator { Width width() final { return {strWidth_, width_}; } - void declare(std::ostream& stream, Language lang, int indent) { + void + declare(std::ostream& stream, Language lang, int indent) { if (lang == Language::VHDL) { - stream << StringTools::indent(indent) << "signal " - << name() << " : "; + stream << StringTools::indent(indent) << "signal " << name() + << " : "; if (width_ < 0 || width_ > 1 || wt_ == WireType::Vector) { if (strWidth_.empty()) { stream << "std_logic_vector(" @@ -339,7 +359,7 @@ namespace HDLGenerator { stream << "std_logic;\n"; } } else if (lang == Language::Verilog) { - stream << StringTools::indent(indent) << "reg "; + stream << StringTools::indent(indent) << "reg "; if (width_ < 0 || width_ > 1) { if (strWidth_.empty()) { stream << "[" << std::to_string(width_ - 1) << ":0] "; @@ -540,14 +560,29 @@ namespace HDLGenerator { class Assign : public SequentialStatement { public: Assign(std::string var, LHSValue value, bool isConstant = false) - : SequentialStatement(var), index_(-1), upperBound_(-1), - lowerBound_(-1), value_(value), isConstant_(isConstant) {} - Assign(std::string var, LHSValue value, int idx, bool isConstant = false) - : SequentialStatement(var), index_(idx), upperBound_(-1), - lowerBound_(-1), value_(value), isConstant_(isConstant) {} - Assign(std::string var, LHSValue value, int ub, int lb, bool isConstant = false) - : SequentialStatement(var), index_(-1), upperBound_(ub), - lowerBound_(lb), value_(value), isConstant_(isConstant) {} + : SequentialStatement(var), + index_(-1), + upperBound_(-1), + lowerBound_(-1), + value_(value), + isConstant_(isConstant) {} + Assign( + std::string var, LHSValue value, int idx, bool isConstant = false) + : SequentialStatement(var), + index_(idx), + upperBound_(-1), + lowerBound_(-1), + value_(value), + isConstant_(isConstant) {} + Assign( + std::string var, LHSValue value, int ub, int lb, + bool isConstant = false) + : SequentialStatement(var), + index_(-1), + upperBound_(ub), + lowerBound_(lb), + value_(value), + isConstant_(isConstant) {} void build() override { Generatable::build(); @@ -580,8 +615,10 @@ namespace HDLGenerator { } } else if (lang == Language::Verilog) { if (isConstant_) { - stream << StringTools::indent(level) << "assign " << name(); - } else if (!(parentIs() || parentIs() )) { + stream << StringTools::indent(level) << "assign " + << name(); + } else if (!(parentIs() || + parentIs())) { stream << StringTools::indent(level) << "always @*\n" << StringTools::indent(level + 1) << name(); } else { @@ -1141,7 +1178,8 @@ namespace HDLGenerator { return *this; } - Behaviour& operator<<(RawCodeLine&& rhs) { + Behaviour& + operator<<(RawCodeLine&& rhs) { addComponent(rhs); return *this; } @@ -1216,7 +1254,8 @@ namespace HDLGenerator { prefix_ = prefix; } - Module& operator<<(RawCodeLine&& rawCodeLine) { + Module& + operator<<(RawCodeLine&& rawCodeLine) { rawCodeLines_.emplace_back(rawCodeLine); return *this; } @@ -1291,6 +1330,11 @@ namespace HDLGenerator { void appendToHeader(const std::string& line) { headerComment_.emplace_back(line); } + // Adding package files + void + setPackages(const std::string& pname) { + packages_.emplace_back(pname); + } virtual bool isRegister(const std::string& name) final { for (auto&& r : registers_) { @@ -1498,7 +1542,7 @@ namespace HDLGenerator { std::string indent = StringTools::indent(level); // Header comment for (auto&& line : headerComment_) { - stream << indent << "-- " << line << "\n"; + stream << indent << "-- " << line << "\n"; } // Libraries stream << indent << "\n" @@ -1508,8 +1552,9 @@ namespace HDLGenerator { << indent << "use ieee.std_logic_misc.all;\n" << indent << "use STD.textio.all;\n" << indent << "use ieee.std_logic_textio.all;\n" - << indent << "use IEEE.math_real.all;\n" - // Entity + << indent + << "use IEEE.math_real.all;\n" + // Entity << indent << "\n" << indent << "entity " << name() << " is\n"; // - Generics @@ -1534,11 +1579,12 @@ namespace HDLGenerator { } stream << ");\n"; } - stream << indent << "end entity " << name() << ";\n" - // Architecture + stream << indent << "end entity " << name() + << ";\n" + // Architecture << indent << "\n" - << indent << "architecture rtl of " - << name() << " is\n"; + << indent << "architecture rtl of " << name() + << " is\n"; // constants if (!constants_.empty() || !binaryConstants_.empty()) { stream << "\n"; @@ -1605,6 +1651,12 @@ namespace HDLGenerator { // Module stream << StringTools::indent(level) << "\n"; stream << StringTools::indent(level) << "module " << name(); + // Packages as imports + if (!packages_.empty()) { + for (auto&& package : packages_) { + stream << "\n import " << package << "::*;\n"; + } + } // - Parameters if (!parameters_.empty()) { std::string separator = ""; @@ -1724,5 +1776,6 @@ namespace HDLGenerator { std::vector > variables_; std::vector> behaviours_; std::vector modules_; + std::vector packages_; }; } diff --git a/openasip/src/applibs/HWGen/HDLPort.hh b/openasip/src/applibs/HWGen/HDLPort.hh index 97a30ace1..88b991049 100755 --- a/openasip/src/applibs/HWGen/HDLPort.hh +++ b/openasip/src/applibs/HWGen/HDLPort.hh @@ -69,7 +69,7 @@ namespace HDLGenerator { if (dir_ == Direction::In) { stream << "input "; } else { - stream << "output reg "; + stream << "output logic "; } if (!parametricWidth_.empty()) { stream << "[" << parametricWidth_ << "-1:0] "; @@ -116,4 +116,4 @@ namespace HDLGenerator { WireType wireType = WireType::Auto) : Port(name, Direction::In, parametricWidth, wireType) {} }; -} \ No newline at end of file +} diff --git a/openasip/src/applibs/HWGen/HDLRegister.hh b/openasip/src/applibs/HWGen/HDLRegister.hh index dc9c38cba..543d775e6 100755 --- a/openasip/src/applibs/HWGen/HDLRegister.hh +++ b/openasip/src/applibs/HWGen/HDLRegister.hh @@ -87,7 +87,7 @@ namespace HDLGenerator { } } else if (lang == Language::Verilog) { if (literal_.name().empty()) { - stream << "0;\n"; + stream << "'0;\n"; } else { literal_.hdl(stream, lang); stream << ";\n"; diff --git a/openasip/src/applibs/HWGen/HWGenTools.hh b/openasip/src/applibs/HWGen/HWGenTools.hh index 8d7497f3f..c32e32661 100755 --- a/openasip/src/applibs/HWGen/HWGenTools.hh +++ b/openasip/src/applibs/HWGen/HWGenTools.hh @@ -30,7 +30,7 @@ #include namespace HDLGenerator { - enum class Language { VHDL, Verilog }; + enum class Language { VHDL, Verilog, SV }; enum class WireType { Auto, Vector }; enum class Direction { In, Out }; enum class ResetOption { Mandatory, Optional }; diff --git a/openasip/src/applibs/HWGen/Makefile.am b/openasip/src/applibs/HWGen/Makefile.am index 467e0f71f..e0e3a0f61 100755 --- a/openasip/src/applibs/HWGen/Makefile.am +++ b/openasip/src/applibs/HWGen/Makefile.am @@ -1,5 +1,5 @@ noinst_LTLIBRARIES = libhwgen.la -libhwgen_la_SOURCES = FUGen.cc RFGen.cc HDLGenerator.cc LHSValue.cc +libhwgen_la_SOURCES = FUGen.cc RFGen.cc HDLGenerator.cc LHSValue.cc TrackerGen.cc SRC_ROOT_DIR = $(top_srcdir)/src @@ -16,6 +16,8 @@ OSAL_DIR = ${BASE_DIR}/osal GRAPH_DIR = ${BASE_DIR}/Graph APPLIBS_PROGE_DIR = ${SRC_ROOT_DIR}/applibs/ProGe PROCGEN_PROGE_DIR = ${SRC_ROOT_DIR}/procgen/ProGe +BEM_DIR = ${APPLIBS_DIR}/bem +BASE_BEM_DIR = ${BASE_DIR}/bem AM_CPPFLAGS = -I${IDF_DIR} \ @@ -29,7 +31,8 @@ AM_CPPFLAGS = -I${IDF_DIR} \ -I${PROCGEN_PROGE_DIR} \ -I$(SRC_ROOT_DIR)/applibs/Explorer \ -I$(SRC_ROOT_DIR)/applibs/dsdb \ - -I$(SRC_ROOT_DIR)/applibs/Simulator + -I$(SRC_ROOT_DIR)/applibs/Simulator \ + -I${BEM_DIR} -I${APPLIBS_DIR}/mach -I${APPLIBS_DIR}/osal -I${BASE_BEM_DIR} dist-hook: @@ -40,6 +43,6 @@ MAINTAINERCLEANFILES = *~ *.gcov *.bbg *.bb *.da ## headers start libhwgen_la_SOURCES += \ FUGen.hh RFGen.hh HDLGenerator.hh HWGenTools.hh HDLRegister.hh \ - HDLPort.hh LHSValue.hh Generatable.hh WidthTransformations.hh \ - BinaryOps.hh + HDLPort.hh LHSValue.hh Generatable.hh WidthTransformations.hh TrackerGen.hh\ + BinaryOps.hh CoproCusops.hh ## headers end diff --git a/openasip/src/applibs/HWGen/TrackerGen.cc b/openasip/src/applibs/HWGen/TrackerGen.cc new file mode 100644 index 000000000..74cc6d383 --- /dev/null +++ b/openasip/src/applibs/HWGen/TrackerGen.cc @@ -0,0 +1,258 @@ +/* + Copyright (C) 2025 Tampere University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ +/** + * @file TrackerGen.hh + * + * Instruction tracker component generation for the CV-X-IF based coprocessors + * + * @author Tharaka Sampath 2024 + */ + +#include "TrackerGen.hh" + +#include + +#include "BinaryOps.hh" +#include "HWGenTools.hh" +#include "HWOperation.hh" +#include "WidthTransformations.hh" + +using namespace HDLGenerator; + +// Easy hack to generate For Loops in Verilog +void +TrackerGen::forLoop( + HDLGenerator::CodeBlock& codeblk, std::string conditionLine, + HDLGenerator::CodeBlock bodyblk) { + RawCodeLine forRawloop("", "for (" + conditionLine + ") begin"); + codeblk.append(forRawloop); + codeblk.append(bodyblk); + codeblk.append(RawCodeLine("", "end")); +} + +// Makes the ports +void +TrackerGen::createPorts() { + tracker_.appendToHeader("CVXIF Instruction Tracker Component"); + + tracker_ << Parameter(HWidth_, 1); + tracker_ << Parameter(IDWidth_, 4); + tracker_ << Parameter(IdBits_, 8); + tracker_ << InPort("clk") << InPort("rstx") + << InPort("commit_hartid_i", HWidth_, WireType::Vector) + << InPort("commit_id_i", IDWidth_, WireType::Vector) + << InPort("commit_kill_i") << InPort("commit_valid_i") + << InPort("output_hartid_i", HWidth_, WireType::Vector) + << InPort("output_id_i", IDWidth_, WireType::Vector) + << InPort("output_valid_i") + << InPort("issue_hartid_i", HWidth_, WireType::Vector) + << InPort("issue_id_i", IDWidth_, WireType::Vector) + << InPort("issue_valid_i"); + // Adding operations to the vector + for (int i = 0; i < adfFU_->operationCount(); ++i) { + TTAMachine::HWOperation* hwop = adfFU_->operation(i); + operations_.emplace_back(hwop->name()); + } + // creating ports for each operation config + for (auto op : operations_) { + tracker_ + << InPort("search_hartid_" + op + "_i", HWidth_, WireType::Vector) + << InPort("search_id_" + op + "_i", IDWidth_, WireType::Vector) + << InPort("search_valid_" + op + "_i") + << OutPort("out_committed_" + op + "_o") + << OutPort("out_killed_" + op + "_o"); + } +} + +void +TrackerGen::createOperation() { + CodeBlock DefaultVals; + CodeBlock SearchBlk; + CodeBlock MemReg; + // signals from commit interface saving + CodeBlock CommitIFsave; + CommitIFsave.append( + If(LHSSignal("(commit_kill_i)"), + Assign( + "kill_mem_d[commit_hartid_i][commit_id_i]", + BinaryLiteral("'1")))); + CommitIFsave.append( + If(BitwiseNot(LHSSignal("commit_kill_i")), + Assign( + "commit_mem_d[commit_hartid_i][commit_id_i]", + BinaryLiteral("'1")))); + If commitIf(LHSSignal("(commit_valid_i)"), CommitIFsave); + + // Committing Operation + CodeBlock commitfor_1; + CodeBlock commitassign_1; + CodeBlock commitif1_innerblk; + commitassign_1.append( + Assign(" commit_mem_d[commit_hartid_i][i]", BinaryLiteral("'1"))); + If commitif1_inner( + LHSSignal( + "((i > issue_mem_r[commit_hartid_i]) & (i <= commit_id_i))"), + commitassign_1); + commitif1_innerblk.append(commitif1_inner); + forLoop( + commitfor_1, "integer i = 0; i <= IdBits; i++", commitif1_innerblk); + If commitif_1( + LHSSignal("(commit_id_i > issue_mem_r[commit_hartid_i])"), + commitfor_1); + + CodeBlock commitfor_2; + CodeBlock commitassign_2; + CodeBlock commitassign2_blk; + commitassign_2.append( + Assign(" commit_mem_d[commit_hartid_i][j]", BinaryLiteral("'1"))); + If commitassign2_inner(LHSSignal("(j <= commit_id_i)"), commitassign_2); + commitassign2_blk.append(commitassign2_inner); + forLoop( + commitfor_2, "integer j = 0; j <= IdBits ; j++", commitassign2_blk); + commitif_1.elseClause(commitfor_2); + + If commitValidIf_1( + LHSSignal("(commit_valid_i & (~ commit_kill_i))"), commitif_1); + + // Killing Operation + CodeBlock killfor_1; + CodeBlock killassign_1; + CodeBlock killassign_2; + CodeBlock killassign1_innerblk; + CodeBlock killassign2_innerblk; + killassign_1.append( + Assign(" kill_mem_d[commit_hartid_i][k]", BinaryLiteral("'1"))); + If killassign1_inner(LHSSignal("(k > commit_id_i)"), killassign_1); + killassign1_innerblk.append(killassign1_inner); + forLoop( + killfor_1, "integer k = 0; k <=tracker_maxval; k++", + killassign1_innerblk); + + killassign_2.append( + Assign(" kill_mem_d[commit_hartid_i][l]", BinaryLiteral("'1"))); + If killassign2_inner( + LHSSignal("(l <= issue_mem_r[commit_hartid_i])"), killassign_2); + killassign2_innerblk.append(killassign2_inner); + forLoop( + killfor_1, "integer l = 0; l <= IdBits; l++", killassign2_innerblk); + If killif_1( + LHSSignal("(commit_id_i > issue_mem_r[commit_hartid_i] )"), + killfor_1); + + CodeBlock killfor_3; + CodeBlock killassign_3; + CodeBlock killassign3_innerblk; + killassign_3.append( + Assign(" kill_mem_d[commit_hartid_i][p]", BinaryLiteral("'1"))); + If killassign3_inner( + LHSSignal("((p >commit_id_i) & (p <= issue_mem_r[commit_hartid_i]))"), + killassign_3); + killassign3_innerblk.append(killassign3_inner); + forLoop( + killfor_3, "integer p = 0; p <= IdBits; p++", killassign3_innerblk); + killif_1.elseClause(killfor_3); + + If killValidIf_2( + LHSSignal("(commit_valid_i & (commit_kill_i))"), killif_1); + + // Removing the ID of the output data + CodeBlock Outputvalid; + Outputvalid.append(Assign( + "commit_mem_d[output_hartid_i][output_id_i]", BinaryLiteral("'0"))); + Outputvalid.append(Assign( + "kill_mem_d[output_hartid_i][output_id_i]", BinaryLiteral("'0"))); + If OutputIf(LHSSignal("(output_valid_i)"), Outputvalid); + // Simultaneous search paths for each operation + for (auto op : operations_) { + CodeBlock Memtraverse; + If Checkcommitmem( + LHSSignal( + "(commit_mem_r[search_hartid_" + op + "_i][search_id_" + op + + "_i])"), + Assign("out_committed_" + op + "_o", BinaryLiteral("'1"))); + Memtraverse.append(Checkcommitmem); + If Checkkillmem( + LHSSignal( + "(kill_mem_r[search_hartid_" + op + "_i][search_id_" + op + + "_i])"), + Assign("out_killed_" + op + "_o", BinaryLiteral("'1"))); + Memtraverse.append(Checkkillmem); + If SearchIf(LHSSignal("(search_valid_" + op + "_i)"), Memtraverse); + DefaultVals.append( + Assign("out_committed_" + op + "_o", BinaryLiteral("'0"))); + DefaultVals.append( + Assign("out_killed_" + op + "_o", BinaryLiteral("'0"))); + SearchBlk.append(SearchIf); + } + // Tracker memory + tracker_ << Register("commit_mem_r", "HWidth-1:0][" + IdBits_); + tracker_ << Wire("commit_mem_d", "HWidth-1:0][" + IdBits_); + MemReg.append(Assign("commit_mem_r", LHSSignal("commit_mem_d"))); + DefaultVals.append(Assign("commit_mem_d", LHSSignal("commit_mem_r"))); + + tracker_ << Register("kill_mem_r", "HWidth-1:0][" + IdBits_); + tracker_ << Wire("kill_mem_d", "HWidth-1:0][" + IdBits_); + MemReg.append(Assign("kill_mem_r", LHSSignal("kill_mem_d"))); + DefaultVals.append(Assign("kill_mem_d", LHSSignal("kill_mem_r"))); + + // Issue ID saving (From the issue interface) + tracker_ << Register("issue_mem_r", "HWidth-1:0][" + IDWidth_); + RawCodeLine issuesave("", "issue_mem_r[issue_hartid_i] <= issue_id_i;"); + If IssueValid(LHSSignal("(issue_valid_i)"), issuesave); + MemReg.append(IssueValid); + + // Memory ID max value calculation + tracker_ << Wire("tracker_maxval", "IdWidth"); + DefaultVals.append(Assign("tracker_maxval", BinaryLiteral("'1"))); + + Synchronous MemRegister("Memory_Registering"); + MemRegister << MemReg; + + Asynchronous TheLogic("Comb_Logic"); + TheLogic << DefaultVals << commitValidIf_1 << killValidIf_2 << OutputIf + << SearchBlk; + + behaviour_ << TheLogic << MemRegister; +} + +void +TrackerGen::createFile() { + tracker_ << behaviour_; + Path dir = Path(options_.outputDirectory) / "systemverilog"; + FileSystem::createDirectory(dir.string()); + Path file = dir / (tracker_.name() + ".sv"); + std::ofstream ofs(file); + tracker_.implement(ofs, Language::Verilog); +} + +/** + * Makes the whole thing + */ +void +TrackerGen::generateTracker( + const ProGeOptions& options, + const std::vector& generatetFUs, + const TTAMachine::Machine& machine) { + for (auto fug : generatetFUs) { + TrackerGen trackerg(options, fug, machine); + trackerg.createPorts(); + trackerg.createOperation(); + trackerg.createFile(); + } +} diff --git a/openasip/src/applibs/HWGen/TrackerGen.hh b/openasip/src/applibs/HWGen/TrackerGen.hh new file mode 100644 index 000000000..fae3072ea --- /dev/null +++ b/openasip/src/applibs/HWGen/TrackerGen.hh @@ -0,0 +1,65 @@ +/* + Copyright (C) 2025 Tampere University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ +/** + * @file TrackerGen.hh + * + * @author Tharaka Sampath 2024 + */ +#pragma once + +#include "FUGenerated.hh" +#include "HDLGenerator.hh" +#include "LHSValue.hh" +#include "Machine.hh" +#include "NetlistBlock.hh" +#include "ProGeOptions.hh" + +class TrackerGen { +public: + TrackerGen() = delete; + TrackerGen( + const ProGeOptions& options, IDF::FUGenerated& fug, + const TTAMachine::Machine& machine) + : options_(options), + tracker_(StringTools::stringToLower("instr_tracker_" + fug.name())), + adfFU_(machine.functionUnitNavigator().item(fug.name())) {} + + static void generateTracker( + const ProGeOptions& options, + const std::vector& generatetFUs, + const TTAMachine::Machine& machine); + +private: + void createPorts(); + void createOperation(); + void createFile(); + void forLoop( + HDLGenerator::CodeBlock& codeblk, std::string conditionLine, + HDLGenerator::CodeBlock bodyblk); + + const ProGeOptions& options_; + std::string trackername_; + HDLGenerator::Module tracker_; + TTAMachine::FunctionUnit* adfFU_; + std::vector operations_; + HDLGenerator::Behaviour behaviour_; + std::string HWidth_ = "HWidth"; + std::string IDWidth_ = "IdWidth"; + std::string IdBits_ = "IdBits"; +}; diff --git a/openasip/src/applibs/HWGen/WidthTransformations.hh b/openasip/src/applibs/HWGen/WidthTransformations.hh old mode 100755 new mode 100644 index bac0fb4c6..e489b82b7 --- a/openasip/src/applibs/HWGen/WidthTransformations.hh +++ b/openasip/src/applibs/HWGen/WidthTransformations.hh @@ -109,4 +109,23 @@ namespace HDLGenerator { verilog_ += "}}"; } }; + + /** + * concat with Splicing of a vector. + */ + class Concatsplice : public LHSValue { + public: + Concatsplice( + std::string name1, LHSValue name2, int upperBound_name1, + int lowerBound_name1) { + readList_.insert(name1); + readList_.insert(name2.vhdl()); + vhdl_ = name1 + "(" + std::to_string(upperBound_name1) + + " downto " + std::to_string(lowerBound_name1) + ") & " + + name2.vhdl(); + verilog_ = "{" + name1 + "[" + std::to_string(upperBound_name1) + + ":" + std::to_string(lowerBound_name1) + "]," + + name2.verilog() + "}"; + } + }; } diff --git a/openasip/src/applibs/LLVMBackend/RISCVTDGen.cc b/openasip/src/applibs/LLVMBackend/RISCVTDGen.cc index 2d87a876e..27e9dee5c 100644 --- a/openasip/src/applibs/LLVMBackend/RISCVTDGen.cc +++ b/openasip/src/applibs/LLVMBackend/RISCVTDGen.cc @@ -49,14 +49,16 @@ #define DEBUG_RISCV_TDGEN 0 -RISCVTDGen::RISCVTDGen(const TTAMachine::Machine& mach) +RISCVTDGen::RISCVTDGen(const TTAMachine::Machine& mach, bool roccEn) : TDGen(mach, false), bem_(NULL) { - bem_ = BEMGenerator(mach).generate(); + bem_ = BEMGenerator(mach, roccEn).generate(); assert(bem_ != NULL); - findCustomOps(); + RISCVTools::findCustomOps(customOps_, bem_); initializeBackendContents(); } +// TODO: OpenASIP converts hex numbers to unsigned by default. This converted +// number might not fit into i32 /** * OpenASIP converts hex numbers to unsigned by default. The converted * number might not fit into i32. This function transforms @@ -67,7 +69,7 @@ RISCVTDGen::RISCVTDGen(const TTAMachine::Machine& mach) */ std::string RISCVTDGen::decimalsToHex(const std::string& pattern) const { - std::regex patternRegex(R"((i32\s)(-?\d+))"); + std::regex patternRegex(R"((XLenVT\s)(-?\d+))"); std::smatch match; std::string modifiedPattern = pattern; @@ -104,30 +106,6 @@ RISCVTDGen::decimalsToHex(const std::string& pattern) const { return modifiedPattern; } - -void -RISCVTDGen::findCustomOps() { - customOps_.clear(); - const std::vector formatsToSearch = { - RISCVFields::RISCV_R_TYPE_NAME, - RISCVFields::RISCV_R1R_TYPE_NAME, - RISCVFields::RISCV_R1_TYPE_NAME, - RISCVFields::RISCV_R3R_TYPE_NAME - }; - for (const std::string& fName : formatsToSearch) { - InstructionFormat* format = bem_->instructionFormat(fName); - if (format == NULL) { - continue; - } - for (int i = 0; i < format->operationCount(); i++) { - const std::string op = format->operationAtIndex(i); - if (!MapTools::containsKey(RISCVFields::RISCVRTypeOperations, op)) { - customOps_.insert({op, format->encoding(op)}); - } - } - } -} - // TODO: make this mapping better so that it works for any numIns std::string @@ -208,6 +186,7 @@ RISCVTDGen::transformTCEPattern(std::string pattern, // Replace register specifiers TCEString patTCEStr = TCEString(pattern); patTCEStr.replaceString("R32IRegs:$op1", "(XLenVT GPR:$rs1)"); + patTCEStr.replaceString("i32 ", "XLenVT "); if (numIns == 3) { patTCEStr.replaceString("R32IRegs:$op2", "(XLenVT GPR:$rs2)"); patTCEStr.replaceString("R32IRegs:$op3", "(XLenVT GPR:$rs3)"); @@ -375,4 +354,4 @@ RISCVTDGen::dumpClassDefinitions(std::ostream& o) const { o << " : OARVInstR1 {\n"; o << "}\n"; -} \ No newline at end of file +} diff --git a/openasip/src/applibs/LLVMBackend/RISCVTDGen.hh b/openasip/src/applibs/LLVMBackend/RISCVTDGen.hh index 31c680f66..5832b42f8 100644 --- a/openasip/src/applibs/LLVMBackend/RISCVTDGen.hh +++ b/openasip/src/applibs/LLVMBackend/RISCVTDGen.hh @@ -41,7 +41,7 @@ class InstructionFormat; class RISCVTDGen : public TDGen { public: - RISCVTDGen(const TTAMachine::Machine& mach); + RISCVTDGen(const TTAMachine::Machine& mach, bool roccEn); virtual ~RISCVTDGen() = default; virtual void generateBackend(const std::string& path) const; virtual std::string generateBackend() const; @@ -49,8 +49,7 @@ public: protected: virtual void initializeBackendContents(); InstructionFormat* findFormat(const std::string name) const; - void findCustomOps(); - + void writeInstructionDeclarations(std::ostream& o) const; void writePatternDefinition(std::ostream& o, Operation& op); void writePatternDefinitions(std::ostream& o); @@ -64,6 +63,8 @@ protected: void dumpClassDefinitions(std::ostream&) const; std::string getFormatType(const std::string& opName) const; + std::string intToHexString(int num) const; + std::string unsignedToHexString(unsigned num) const; std::string decimalsToHex(const std::string& pattern) const; BinaryEncoding* bem_; @@ -73,5 +74,4 @@ protected: }; - -#endif \ No newline at end of file +#endif diff --git a/openasip/src/applibs/ProGe/CoproGen.cc b/openasip/src/applibs/ProGe/CoproGen.cc new file mode 100644 index 000000000..f01163592 --- /dev/null +++ b/openasip/src/applibs/ProGe/CoproGen.cc @@ -0,0 +1,412 @@ +/* + Copyright (C) 2025 Tampere University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ +/** + * @file CoproGen.cc derived from ProcessorGenerator + */ + +#include "CoproGen.hh" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AddressSpace.hh" +#include "Application.hh" +#include "BEMGenerator.hh" +#include "BinaryEncoding.hh" +#include "BlockSourceCopier.hh" +#include "ControlUnit.hh" +#include "Conversion.hh" +#include "Environment.hh" +#include "FUGen.hh" +#include "FUPort.hh" +#include "FUPortCode.hh" +#include "FileSystem.hh" +#include "FunctionUnit.hh" +#include "HDBManager.hh" +#include "HDBRegistry.hh" +#include "HWOperation.hh" +#include "ICDecoderGeneratorPlugin.hh" +#include "Machine.hh" +#include "MachineImplementation.hh" +#include "MachineInfo.hh" +#include "MachineResourceModifier.hh" +#include "MachineValidator.hh" +#include "MachineValidatorResults.hh" +#include "MathTools.hh" +#include "Netlist.hh" +#include "NetlistBlock.hh" +#include "NetlistFactories.hh" +#include "NetlistGenerator.hh" +#include "NetlistPort.hh" +#include "NetlistPortGroup.hh" +#include "NetlistTools.hh" +#include "NetlistVisualization.hh" +#include "ProGeContext.hh" +#include "ProGeOptions.hh" +#include "RFArchitecture.hh" +#include "RFEntry.hh" +#include "SpecialRegisterPort.hh" +#include "StringTools.hh" +#include "TrackerGen.hh" +#include "VHDLNetlistWriter.hh" +#include "VerilogNetlistWriter.hh" + +using boost::format; +using std::endl; +using std::set; +using std::string; +using namespace TTAMachine; +using namespace IDF; +using namespace HDB; + +namespace ProGe { + +/** + * The constructor. + */ +CoproGen::CoproGen() : coreTopBlock_(NULL) {} + +/** + * The destructor. + */ +CoproGen::~CoproGen() { + delete coreTopBlock_; + coreTopBlock_ = NULL; +} + +/** + * Generates the coprocessor. + * + * @see ProGeUI::coproGenerate() + */ +void +CoproGen::coproGenerate( + const ProGeOptions& options, const TTAMachine::Machine& machine, + const IDF::MachineImplementation& implementation, + ProGe::ICDecoderGeneratorPlugin& plugin, int imemWidthInMAUs, + std::ostream& errorStream, std::ostream& warningStream, + std::ostream& verboseStream) { + entityStr_ = options.entityName; + generatorContext_ = new ProGe::ProGeContext( + machine, implementation, options.outputDirectory, + options.sharedOutputDirectory, options.entityName, options.language, + imemWidthInMAUs); + + // validate the machine + validateMachine(machine, errorStream, warningStream); + + ProGe::NetlistGenerator netlistGenerator(*generatorContext_, plugin); + // HDLTemplateInstantiator HDLTemplateInstantiator(*generatorContext_); + coreTopBlock_ = netlistGenerator.generatecopro( + options, imemWidthInMAUs, options.entityName, warningStream); + + bool created = FileSystem::createDirectory(options.outputDirectory); + if (!created) { + string errorMsg = + "Unable to create directory " + options.outputDirectory; + throw IOException(__FILE__, __LINE__, __func__, errorMsg); + } + + // Generate generatable FU implementations. + std::vector globalOptions; + globalOptions.emplace_back("active low reset"); + globalOptions.emplace_back("asynchronous reset"); + globalOptions.emplace_back("reset everything"); + + // Implementing FUs + FUGen::implement( + options, globalOptions, generatorContext_->idf().FUGenerations(), + generatorContext_->adf(), coreTopBlock_); + + string topLevelDir = + options.outputDirectory + FileSystem::DIRECTORY_SEPARATOR + + (options.language == ProGe::VHDL ? "vhdl" : "systemverilog"); + + if (Application::spamVerbose()) + ProGe::NetlistVisualization::visualizeBlockTree( + *coreTopBlock_, verboseStream); + + if (!FileSystem::fileExists(options.sharedOutputDirectory)) { + if (!FileSystem::createDirectory(options.sharedOutputDirectory)) { + string errorMsg = "Unable to create directory " + + options.sharedOutputDirectory + "."; + throw IOException(__FILE__, __LINE__, __func__, errorMsg); + } + } + // Makes CVX Coprocessor TOP for each FU + if (options.roccGen == true) { + for (auto Fu : generatorContext_->idf().FUGenerations()) { + makeROCCcoprocessor(options, Fu, generatorContext_->adf()); + } + } else { + for (auto Fu : generatorContext_->idf().FUGenerations()) { + makecoprocessor(options, Fu, generatorContext_->adf()); + } + generateinsdecoder(options); // Generates Compressed decoder + generateSupPackage( + options.outputDirectory); // Generates the support package + // Implementing Instruction Tracker + TrackerGen::generateTracker( + options, generatorContext_->idf().FUGenerations(), + generatorContext_->adf()); + } +} + +/* + * Makes the top of the ROCC Coprocessor + */ +void +CoproGen::makeROCCcoprocessor( + const ProGeOptions& options, IDF::FUGenerated& Fu, + const TTAMachine::Machine& machine) { + TTAMachine::FunctionUnit* adfFU = + machine.functionUnitNavigator().item(Fu.name()); + const std::string dstDirectory = + options.outputDirectory; // destination directory + const std::string DS = FileSystem::DIRECTORY_SEPARATOR; + std::string coproTargetDir = dstDirectory; + if (!FileSystem::fileExists(coproTargetDir)) { + if (!FileSystem::createDirectory(coproTargetDir)) { + std::string errorMsg = + "Unable to create directory " + coproTargetDir; + throw IOException(__FILE__, __LINE__, __func__, errorMsg); + } + } + std::string sourceFile; + std::string dstFile; + sourceFile = + Environment::dataDirPath("ProGe") + DS + "rocc_copro.sv.tmpl"; + std::string file = + StringTools::stringToLower("coprocessor_" + Fu.name()) + ".sv"; + dstFile = coproTargetDir + DS + "systemverilog/" + file; + std::string replcements1[8], replcements2[8]; // replacement keys + replcements1[0] = {"FUNAME"}; + replcements2[0] = StringTools::stringToLower(Fu.name()); + + if (!FileSystem::fileExists(dstFile)) { + instantiate_.instantiateCoprocessorTemplateFile( + sourceFile, dstFile, replcements1, replcements2, 1); + } +} + +/* + * Makes the top of the Coprocessor + */ +void +CoproGen::makecoprocessor( + const ProGeOptions& options, IDF::FUGenerated& Fu, + const TTAMachine::Machine& machine) { + TTAMachine::FunctionUnit* adfFU = + machine.functionUnitNavigator().item(Fu.name()); + const std::string dstDirectory = + options.outputDirectory; // destination directory + const std::string DS = FileSystem::DIRECTORY_SEPARATOR; + std::string coproTargetDir = dstDirectory; + if (!FileSystem::fileExists(coproTargetDir)) { + if (!FileSystem::createDirectory(coproTargetDir)) { + std::string errorMsg = + "Unable to create directory " + coproTargetDir; + throw IOException(__FILE__, __LINE__, __func__, errorMsg); + } + } + std::string sourceFile; + std::string dstFile; + sourceFile = + Environment::dataDirPath("ProGe") + DS + "cvxif_coprocessor.sv.tmpl"; + std::string file = + StringTools::stringToLower(Fu.name()) + "_coprocessor.sv"; + dstFile = coproTargetDir + DS + "systemverilog/" + file; + std::string coproreplcements1[8] = { + "FUNAME", "INPUT1", "OUTPUTF", + "CONFIG_EN", "CONFIG_DEFINE", "OUT_COMMIT_TRACKER", + "CONFIG_BITS", "SEARCH_CONFIG"}; // replacement keys REMOVE INPUT2 + std::string coproreplcements2[8]; + coproreplcements2[0] = StringTools::stringToLower(Fu.name()); + int j = 0; + int k = 1; + for (int i = 0; i < adfFU->portCount(); + ++i) { // iterating through ports in the FU + TTAMachine::FUPort* adfPort = + static_cast(adfFU->port(i)); + if (adfPort->isInput()) { + j++; + if (j == 4) { // TODO:Make it to handle the number of inputs with + // a parameter which can be controlled + throw std::runtime_error( + " More than 3 input ports are in the FU"); + } else { + coproreplcements2[1] = + coproreplcements2[1] + ".data_" + adfPort->name() + + "_in(register.rs[" + std::to_string(j - 1) + + "]),\n"; // adding input port names to the array + } + } else { + k++; + if (k == 4) { + throw std::runtime_error( + " More than 1 output port in the FU"); + } else { + coproreplcements2[k] = + adfPort->name(); // adding Output names + } + } + } + for (int i = 0; i < adfFU->operationCount(); ++i) { + TTAMachine::HWOperation* hwop = adfFU->operation(i); + std::string op = hwop->name(); + + coproreplcements2[3] = coproreplcements2[3] + ".config_" + op + + "_enable_in(commit_" + op + + "_tracker_to_FU),\n" + ".config_" + op + + "_kill_in(kill_" + op + "_tracker_to_FU),\n"; + coproreplcements2[4] = + coproreplcements2[4] + "logic commit_" + op + + "_tracker_to_FU;\n" + "logic kill_" + op + "_tracker_to_FU;\n" + + "logic [cvxif_sup_pkg::NConfigbits_C-1 : 0] configbits_" + op + + "_to_tracker; \n"; + coproreplcements2[5] = coproreplcements2[5] + ".out_committed_" + op + + "_o(commit_" + op + "_tracker_to_FU),\n" + + ".out_killed_" + op + "_o(kill_" + op + + "_tracker_to_FU),\n"; + coproreplcements2[6] = coproreplcements2[6] + ".configbits_" + op + + "_out(configbits_" + op + "_to_tracker),\n"; + coproreplcements2[7] = + coproreplcements2[7] + ".search_id_" + op + "_i(configbits_" + + op + "_to_tracker[cvxif_sup_pkg::X_ID_WIDTH - 1 : 0]),\n" + + ".search_hartid_" + op + "_i(configbits_" + op + + "_to_tracker[cvxif_sup_pkg::X_ID_WIDTH + " + "cvxif_sup_pkg::X_HARTID_WIDTH - 1 : " + "cvxif_sup_pkg::X_ID_WIDTH]),\n" + + ".search_valid_" + op + "_i(configbits_" + op + + "_to_tracker[cvxif_sup_pkg::NConfigbits_C-1]),\n"; + } + + if (!FileSystem::fileExists(dstFile)) { + instantiate_.instantiateCoprocessorTemplateFile( + sourceFile, dstFile, coproreplcements1, coproreplcements2, 8); + } +} + +// Instantiates the compressed decoder TODO +void +CoproGen::generateinsdecoder(const ProGeOptions& options) { + const std::string DS = FileSystem::DIRECTORY_SEPARATOR; + const std::string dstDirectory = options.outputDirectory; + std::string sourceFile; + std::string sourceFileComp; + std::string dstFile; + std::string dstFileComp; + + sourceFileComp = Environment::dataDirPath("ProGe") + DS + + "cvxifcompressed_decoder.sv.tmpl"; + + std::string fileComp = "cvxifcompressed_decoder.sv"; + dstFileComp = dstDirectory + DS + "systemverilog/" + fileComp; + + FileSystem::copy(sourceFileComp, dstFileComp); +} + +/** + * Instruction opcode support package generator. + * @param language The langauge:currently only verilog. + * @param dstDirectory The destination directory. + */ +void +CoproGen::generateSupPackage(const std::string& dstDirectory) { + string dstFile = dstDirectory + FileSystem::DIRECTORY_SEPARATOR + + "systemverilog/cvxif_sup_pkg.sv"; + bool created = FileSystem::createFile(dstFile); + if (!created) { + string errorMsg = "Unable to create file " + dstFile; + throw IOException(__FILE__, __LINE__, __func__, errorMsg); + } + std::ofstream stream(dstFile.c_str(), std::ofstream::out); + stream << "package cvxif_sup_pkg;" << endl + << " parameter int unsigned X_RFW_WIDTH = " + "cva6_config_pkg::CVA6ConfigXlen;" + << endl + << " parameter int unsigned X_HARTID_WIDTH = " + "cva6_config_pkg::CVA6ConfigXlen;" + << endl + << " parameter int unsigned X_DUALWRITE = 0;" << endl + << " parameter int unsigned X_ID_WIDTH = " + "$clog2(cva6_config_pkg::cva6_cfg.NrScoreboardEntries);" + << endl + << " parameter int unsigned IdBits = 2**X_ID_WIDTH;" << endl + << " parameter int unsigned NConfigbits_C = 1 + X_ID_WIDTH + " + "X_HARTID_WIDTH + 5 + (X_DUALWRITE + 1);" + << endl + << " parameter logic [31:0] OpcodeMask = " + "32'b11111_11_00000_00000_111_00000_1111111;" + << endl // For R type ones + << "endpackage" << endl; + stream.close(); +} + +/** + * Validates the machine for compatibility with the given block + * implementations. + * + * If the target architecture contains errors (incomplete definition) or + * if its structure is not compatible with this HDL generator, this method + * throws IllegalMachine exception. For less serious errors (such as + * non-critical bit width discrepancies), warning messages are written to + * the given stream. + * + * @param machine The machine to validate. + * @param errorStream Output stream where errors are printed + * @param warningStream Output stream where warnings are printed + * @exception IllegalMachine If there is a fundamental error in the + * machine. + */ +void +CoproGen::validateMachine( + const TTAMachine::Machine& machine, std::ostream& errorStream, + std::ostream& warningStream) { + MachineValidator validator(machine); + set errorsToCheck; + errorsToCheck.insert(MachineValidator::USED_IO_NOT_BOUND); + errorsToCheck.insert(MachineValidator::FU_PORT_MISSING); + + MachineValidatorResults* results = validator.validate(errorsToCheck); + + for (int i = 0; i < results->errorCount(); i++) { + MachineValidator::ErrorCode code = results->error(i).first; + string errorMsg = results->error(i).second; + if (code == MachineValidator::USED_IO_NOT_BOUND) { + warningStream << "Warning: " << errorMsg << endl; + delete results; + throw IllegalMachine(__FILE__, __LINE__, __func__, "Error"); + } else { + string msg = "Error: " + errorMsg; + errorStream << msg << std::endl; + delete results; + throw IllegalMachine(__FILE__, __LINE__, __func__, msg); + } + } + delete results; +} + +} // namespace ProGe diff --git a/openasip/src/applibs/ProGe/CoproGen.hh b/openasip/src/applibs/ProGe/CoproGen.hh new file mode 100644 index 000000000..d21499335 --- /dev/null +++ b/openasip/src/applibs/ProGe/CoproGen.hh @@ -0,0 +1,101 @@ +/* + Copyright (C) 2025 Tampere University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ +/** + * @file CoProGe.hh + * + * Declaration of Co Processor Generator class. + * + * @author Lasse Laasonen 2005 (lasse.laasonen-no.spam-tut.fi) + * @author Otto Esko 2010 (otto.esko-no.spam-tut.fi) + * @author Pekka Jääskeläinen 2011 + * @author Vinogradov Viacheslav(added SV generating) 2012 + * @note rating: red + */ + +#ifndef COPROCESSOR_GENERATOR_HH +#define COPROCESSOR_GENERATOR_HH + +#include "Exception.hh" +#include "HDLTemplateInstantiator.hh" +#include "ProGeContext.hh" +#include "ProGeOptions.hh" +#include "ProGeTypes.hh" +#include "ProcessorGenerator.hh" +#include "TCEString.hh" + +namespace TTAMachine { +class Machine; +class FunctionUnit; +} // namespace TTAMachine + +namespace IDF { +class MachineImplementation; +} + +class BinaryEncoding; +class FUPortCode; + +namespace ProGe { + +class ICDecoderGeneratorPlugin; +class Netlist; +class NetlistBlock; +class ProGeContext; +class NetlistGenerator; + +/** + * Controller class of ProGe. + */ +class CoproGen : public ProcessorGenerator { +public: + CoproGen(); + virtual ~CoproGen(); + + void coproGenerate( + const ProGeOptions& options, const TTAMachine::Machine& machine, + const IDF::MachineImplementation& implementation, + ICDecoderGeneratorPlugin& plugin, int imemWidthInMAUs, + std::ostream& errorStream, std::ostream& warningStream, + std::ostream& verboseStream); + +private: + void validateMachine( + const TTAMachine::Machine& machine, std::ostream& errorStream, + std::ostream& warningStream); + void generateSupPackage(const std::string& dstDirectory); + void generateinsdecoder( + const ProGeOptions& options); // Instruciton decoder maker + void makecoprocessor( + const ProGeOptions& options, IDF::FUGenerated& Fu, + const TTAMachine::Machine& machine); + void makeROCCcoprocessor( + const ProGeOptions& options, IDF::FUGenerated& Fu, + const TTAMachine::Machine& machine); + + NetlistBlock* coreTopBlock_; + TCEString entityStr_; + ProGeContext* generatorContext_; + + static const TCEString DEFAULT_ENTITY_STR; + /// Object that instantiates templates. + HDLTemplateInstantiator instantiate_; +}; +} // namespace ProGe + +#endif diff --git a/openasip/src/applibs/ProGe/HDLTemplateInstantiator.cc b/openasip/src/applibs/ProGe/HDLTemplateInstantiator.cc index 554382d22..ccabef0c4 100644 --- a/openasip/src/applibs/ProGe/HDLTemplateInstantiator.cc +++ b/openasip/src/applibs/ProGe/HDLTemplateInstantiator.cc @@ -139,6 +139,44 @@ HDLTemplateInstantiator::instantiateTemplateFile( output.close(); } +/** + * Creates a target HDL file from a HDL template replacing Nelements number of + * keywords + */ +void +HDLTemplateInstantiator::instantiateCoprocessorTemplateFile( + const std::string& templateFile, const std::string& dstFile, + const std::string (&coproreplcement_keys)[8], + const std::string (&coproreplcement_words)[8], int Nelements) { + std::ifstream input(templateFile.c_str()); + + if (!input.is_open()) + throw UnreachableStream( + __FILE__, __LINE__, __func__, + TCEString("Could not open ") + templateFile + " for reading."); + + std::ofstream output(dstFile.c_str(), std::ios::trunc); + + if (!output.is_open()) + throw UnreachableStream( + __FILE__, __LINE__, __func__, + TCEString("Could not open ") + dstFile + " for writing. CVXIF"); + + while (!input.eof()) { + char line_buf[1024]; + input.getline(line_buf, 1024); + TCEString line(line_buf); + for (int i = 0; i < Nelements; i++) { + line.replaceString( + coproreplcement_keys[i], coproreplcement_words[i]); + } + fillPlaceholders(line); + output << line << std::endl; + } + input.close(); + output.close(); +} + /** * Fills all placeholder templates with added replace strings by key. * diff --git a/openasip/src/applibs/ProGe/HDLTemplateInstantiator.hh b/openasip/src/applibs/ProGe/HDLTemplateInstantiator.hh index 145db15a0..4d082cae5 100644 --- a/openasip/src/applibs/ProGe/HDLTemplateInstantiator.hh +++ b/openasip/src/applibs/ProGe/HDLTemplateInstantiator.hh @@ -60,6 +60,11 @@ public: void instantiateTemplateFile( const std::string& templateFile, const std::string& dstFile); + // For implementing CVXIF coprocessor specific templates + void instantiateCoprocessorTemplateFile( + const std::string& templateFile, const std::string& dstFile, + const std::string (&coproreplcement_keys)[8], + const std::string (&coproreplcement_words)[8], int Nelements); private: typedef TCEString PlaceholderKey; diff --git a/openasip/src/applibs/ProGe/Makefile.am b/openasip/src/applibs/ProGe/Makefile.am index 99954604b..4614bd28e 100644 --- a/openasip/src/applibs/ProGe/Makefile.am +++ b/openasip/src/applibs/ProGe/Makefile.am @@ -12,8 +12,8 @@ NetlistPortGroup.cc SignalGroupDefinitions.cc \ ProGeContext.cc GlobalPackage.cc \ TestBenchBlock.cc SinglePortSSRAMBlock.cc \ ProcessorWrapperBlock.cc LoopBufferBlock.cc MemoryBusInterface.cc \ -ProGeTools.cc RV32MicroCodeGenerator.cc \ -SinglePortByteMaskSSRAMBlock.cc +ProGeTools.cc RV32MicroCodeGenerator.cc CoproGen.cc\ +SinglePortByteMaskSSRAMBlock.cc SRC_ROOT_DIR = $(top_srcdir)/src @@ -79,7 +79,7 @@ libproge_la_SOURCES += \ GeneratableRFNetlistBlock.hh \ OperationPool.hh Operation.hh Operand.hh \ IPXact.hh ProGeCmdLineOptions.hh ProGeTools.hh \ - MicroCodeGenerator.hh RV32MicroCodeGenerator.hh \ + MicroCodeGenerator.hh RV32MicroCodeGenerator.hh CoproGen.hh\ SinglePortByteMaskSSRAMBlock.hh ## headers end diff --git a/openasip/src/applibs/ProGe/NetlistBlock.cc b/openasip/src/applibs/ProGe/NetlistBlock.cc index 487d23220..f6f68d5d4 100644 --- a/openasip/src/applibs/ProGe/NetlistBlock.cc +++ b/openasip/src/applibs/ProGe/NetlistBlock.cc @@ -210,6 +210,10 @@ NetlistBlock::write(const Path& targetBaseDir, HDL targetLang) const { writer = new VerilogNetlistWriter(*this); topLevelDir = targetBaseDir.string() + FileSystem::DIRECTORY_SEPARATOR + "verilog"; + } else if (targetLang == ProGe::SV) { + writer = new VerilogNetlistWriter(*this, targetLang); + topLevelDir = targetBaseDir.string() + + FileSystem::DIRECTORY_SEPARATOR + "systemverilog"; } else { assert(false && "Unsupported HDL."); } diff --git a/openasip/src/applibs/ProGe/NetlistGenerator.cc b/openasip/src/applibs/ProGe/NetlistGenerator.cc index 85dd089a5..6417ed23d 100644 --- a/openasip/src/applibs/ProGe/NetlistGenerator.cc +++ b/openasip/src/applibs/ProGe/NetlistGenerator.cc @@ -240,6 +240,41 @@ namespace ProGe { return coreBlock_; } + /** + * Generates the netlist block of the coprocessor. + * + * @param imemWidthInMAUs Width of instruction memory in MAUs. + * @param entityNameStr The name string used to make the netlist blocks + * uniquely named. + * @return The newly generated netlist block. + */ + NetlistBlock* + NetlistGenerator::generatecopro( + const ProGeOptions& options, int imemWidthInMAUs, + TCEString entityNameStr = TOPLEVEL_BLOCK_DEFAULT_NAME, + std::ostream& warningStream = std::cerr) { + if (imemWidthInMAUs < 1) { + string errorMsg = + "Instruction memory width in MAUs must be positive."; + throw OutOfRange(__FILE__, __LINE__, __func__, errorMsg); + } + + // add toplevel block + coreBlock_ = new NetlistBlock(entityNameStr, "CVXIF_TOP"); + coreBlock_->addPackage("cvxif_"); + coreBlock_->addPackage("cvxif_instr_"); + + addFUPortstoNetlist(*coreBlock_); + + // add Genrated FUs to the toplevel netlist. + for (auto fug : context_.idf().FUGenerations()) { + addGeneratableFUsToNetlist(fug, *coreBlock_); //MODIFY + } + + //plugin_.completeNetlist(*coreBlock_, *this); + + return coreBlock_; + } /** * Returns the netlist port which is corresponding to the given port in * the @@ -2255,5 +2290,27 @@ namespace ProGe { } return SignalType::UNDEFINED; } + + /** + * Adds the Fu ports to the given //TODO + * top-level block. + * + * @param toplevelBlock The top-level block of the netlist. + * @param + */ + void NetlistGenerator::addFUPortstoNetlist( + NetlistBlock& toplevelBlock){ + + NetlistPort* tlClkPort = + new InBitPort(CLOCK_PORT_NAME, toplevelBlock, SignalType::CLOCK); + NetlistPort* tlRstPort = new InBitPort(RESET_PORT_NAME, toplevelBlock, + Signal(SignalType::RESET, ActiveState::LOW)); + + mapClockPort(toplevelBlock, *tlClkPort); + mapResetPort(toplevelBlock, *tlRstPort); + Netlist::connectClocks(toplevelBlock); + Netlist::connectResets(toplevelBlock); + + } } // namespace ProGe diff --git a/openasip/src/applibs/ProGe/NetlistGenerator.hh b/openasip/src/applibs/ProGe/NetlistGenerator.hh index add42de00..16237ad00 100644 --- a/openasip/src/applibs/ProGe/NetlistGenerator.hh +++ b/openasip/src/applibs/ProGe/NetlistGenerator.hh @@ -95,6 +95,10 @@ namespace ProGe { NetlistBlock* generate( const ProGeOptions& options, int imemWidthInMAUs, TCEString entityNameStr, std::ostream& warningStream); + // Generate CVXIF coprocessor + NetlistBlock* generatecopro( + const ProGeOptions& options, int imemWidthInMAUs, + TCEString entityNameStr, std::ostream& warningStream); NetlistPort& netlistPort( const TTAMachine::Port& port, Direction dir = IN) const; @@ -248,6 +252,8 @@ namespace ProGe { static TTAMachine::AddressSpace& instructionMemory( const TTAMachine::Machine& machine); static Direction translateDirection(HDB::Direction direction); + void addFUPortstoNetlist( + NetlistBlock& toplevelBlock); // Maps generated FUs ports const ProGeContext& context_; /// The generator plugin. diff --git a/openasip/src/applibs/ProGe/ProGeOptions.hh b/openasip/src/applibs/ProGe/ProGeOptions.hh index 139cd9b84..c78e2950f 100644 --- a/openasip/src/applibs/ProGe/ProGeOptions.hh +++ b/openasip/src/applibs/ProGe/ProGeOptions.hh @@ -1,5 +1,5 @@ /* - Copyright (c) 2002-2017 Tampere University. + Copyright (c) 2002-2025 Tampere University. This file is part of TTA-Based Codesign Environment (TCE). @@ -31,13 +31,15 @@ #pragma once -#include "FileSystem.hh" -#include "ProGeCmdLineOptions.hh" -#include "ProGeTypes.hh" #include #include #include +#include "CoprocessorCmdLineOptions.hh" //For CVXIF CMD options +#include "FileSystem.hh" +#include "ProGeCmdLineOptions.hh" +#include "ProGeTypes.hh" + struct ProGeOptions { ProGeOptions() @@ -50,23 +52,31 @@ struct ProGeOptions { ProGeOptions(const ProGeCmdLineOptions& cmd) : processorToGenerate(cmd.processorToGenerate()), - bemFile(cmd.bemFile()), idfFile(cmd.idfFile()), - languageStr(cmd.hdl()), outputDirectory(cmd.outputDirectory()), + bemFile(cmd.bemFile()), + idfFile(cmd.idfFile()), + languageStr(cmd.hdl()), + outputDirectory(cmd.outputDirectory()), sharedOutputDirectory(cmd.sharedOutputDirectory()), pluginParametersQuery(cmd.pluginParametersQuery()), generateTestbench(cmd.generateTestbench()), - integratorName(cmd.integratorName()), imemType(cmd.imemType()), - dmemType(cmd.dmemType()), clockFrequency(cmd.clockFrequency()), - tpefName(cmd.tpefName()), entityName(cmd.entityName()), + integratorName(cmd.integratorName()), + imemType(cmd.imemType()), + dmemType(cmd.dmemType()), + clockFrequency(cmd.clockFrequency()), + tpefName(cmd.tpefName()), + entityName(cmd.entityName()), useAbsolutePaths(cmd.useAbsolutePaths()), listAvailableIntegrators(cmd.listAvailableIntegrators()), deviceFamilyName(cmd.deviceFamilyName()), deviceName(cmd.deviceName()), simulationRuntime(cmd.simulationRuntime()), forceOutputDirectory(cmd.forceOutputDirectory()), - asyncReset(cmd.asyncReset()), syncReset(cmd.syncReset()), - hdbList(cmd.hdbList()), rfIcGateList(cmd.rfIcGateList()), - fuIcGateList(cmd.fuIcGateList()), icdArgList(cmd.icdArgList()), + asyncReset(cmd.asyncReset()), + syncReset(cmd.syncReset()), + hdbList(cmd.hdbList()), + rfIcGateList(cmd.rfIcGateList()), + fuIcGateList(cmd.fuIcGateList()), + icdArgList(cmd.icdArgList()), preferHDLGeneration(cmd.preferHDLGeneration()), resetAllRegisters(cmd.resetAllRegisters()), fuBackRegistered(cmd.fuBackRegistered()), @@ -76,6 +86,20 @@ struct ProGeOptions { validate(); } + // Constructor for CVXIF stuff + ProGeOptions(const CoprocessorCmdLineOptions& cmd, bool CVXIFEN) + : processorToGenerate(cmd.processorToGenerate()), + bemFile(cmd.bemFile()), + idfFile(cmd.idfFile()), + languageStr("sv"), + outputDirectory(cmd.outputDirectory()), + preferHDLGeneration(true), + entityName(cmd.entityName()), + coproInterface(cmd.interFace()), + hdbList(cmd.hdbList()) { + validate(); + } + std::string processorToGenerate; std::string bemFile; std::string idfFile; @@ -86,6 +110,7 @@ struct ProGeOptions { std::string pluginParametersQuery; bool generateTestbench; + std::string coproInterface; std::string integratorName; std::string imemType; std::string dmemType; @@ -111,7 +136,10 @@ struct ProGeOptions { std::vector fuFrontRegistered; std::vector fuMiddleRegistered; bool dontCareInitialization; - + // To enable CVXIF coprocessor generator + bool CVXIFCoproGen; + // To enable ROCC coprocessor generation + bool roccGen; void validate() { if (outputDirectory.empty()) { @@ -128,6 +156,8 @@ struct ProGeOptions { } if (languageStr == "verilog") { language = ProGe::HDL::Verilog; + } else if (languageStr == "sv") { + language = ProGe::HDL::SV; } else { language = ProGe::HDL::VHDL; } @@ -136,7 +166,16 @@ struct ProGeOptions { hdbList.emplace_back("generate_lsu_32.hdb"); hdbList.emplace_back("generate_rf_iu.hdb"); hdbList.emplace_back("asic_130nm_1.5V.hdb"); - + } + if (coproInterface == "rocc") { + CVXIFCoproGen = false; + roccGen = true; + } else if (coproInterface == "cvx") { + CVXIFCoproGen = true; + roccGen = false; + } else { + CVXIFCoproGen = false; + roccGen = false; } } }; diff --git a/openasip/src/applibs/ProGe/ProGeTypes.hh b/openasip/src/applibs/ProGe/ProGeTypes.hh index 31dcf8d1b..af7a2e0eb 100644 --- a/openasip/src/applibs/ProGe/ProGeTypes.hh +++ b/openasip/src/applibs/ProGe/ProGeTypes.hh @@ -38,8 +38,9 @@ namespace ProGe { /// HDLs supported by ProGe. enum HDL { - VHDL=0, ///< VHDL - Verilog ///< Verilog + VHDL = 0, ///< VHDL + Verilog, ///< Verilog + SV ///< SystemVerilog }; /// Data types of hardware ports. diff --git a/openasip/src/applibs/ProGe/ProGeUI.cc b/openasip/src/applibs/ProGe/ProGeUI.cc index c2f2eeb41..99efb7e53 100644 --- a/openasip/src/applibs/ProGe/ProGeUI.cc +++ b/openasip/src/applibs/ProGe/ProGeUI.cc @@ -34,49 +34,44 @@ * @note rating: red */ +#include "ProGeUI.hh" + +#include #include #include #include -#include - -#include "ProGeUI.hh" -#include "ProGeTypes.hh" -#include "ProcessorGenerator.hh" -#include "TestBenchBlock.hh" -#include "Machine.hh" #include "ADFSerializer.hh" -#include "ControlUnit.hh" -#include "SpecialRegisterPort.hh" -#include "FUPort.hh" - -#include "BinaryEncoding.hh" -#include "BEMSerializer.hh" +#include "AlmaIFIntegrator.hh" +#include "AvalonIntegrator.hh" #include "BEMGenerator.hh" +#include "BEMSerializer.hh" #include "BEMValidator.hh" - -#include "MachineImplementation.hh" -#include "IDFSerializer.hh" -#include "IDFValidator.hh" - -#include "ProcessorConfigurationFile.hh" +#include "BinaryEncoding.hh" +#include "ControlUnit.hh" +#include "CoproGen.hh" //CVXIF coprocessor generator #include "Environment.hh" +#include "FUPort.hh" #include "FileSystem.hh" - -#include "ProGeScriptGenerator.hh" -#include "ProGeTestBenchGenerator.hh" - +#include "IDFSerializer.hh" +#include "IDFValidator.hh" +#include "KoskiIntegrator.hh" +#include "Machine.hh" +#include "MachineImplementation.hh" #include "MathTools.hh" #include "Netlist.hh" #include "NetlistBlock.hh" #include "PlatformIntegrator.hh" +#include "ProGeScriptGenerator.hh" +#include "ProGeTestBenchGenerator.hh" +#include "ProGeTools.hh" +#include "ProGeTypes.hh" +#include "ProcessorConfigurationFile.hh" +#include "ProcessorGenerator.hh" +#include "SpecialRegisterPort.hh" #include "Stratix2DSPBoardIntegrator.hh" #include "Stratix3DevKitIntegrator.hh" -#include "KoskiIntegrator.hh" -#include "AvalonIntegrator.hh" -#include "AlmaIFIntegrator.hh" - -#include "ProGeTools.hh" +#include "TestBenchBlock.hh" using namespace IDF; using std::string; @@ -115,6 +110,21 @@ ProGeUI::~ProGeUI() { } } +/** + * Removes FUs from the IDF for the RISCV Coprocessors + */ +void +ProGeUI::removeFUsIDF(IDF::MachineImplementation* idf) { + const std::vector fuNames = { + "alu", "lsu", "mul_div", "ALU", "LSU", "MUL_DIV", "stdout"}; + for (std::string fuName : fuNames) { + if (idf->hasFUGeneration(fuName)) { + idf->removeFuGeneration(fuName); + } else if (idf->hasFUImplementation(fuName)) { + idf->removeFUImplementation(fuName); + } + } +} /** * Loads machine from the given ADF file. @@ -355,9 +365,18 @@ ProGeUI::generateProcessor( *machine_, warningStream); try { - generator_.generateProcessor( - options, *machine_, *idf_, *plugin_, imemWidthInMAUs, errorStream, - warningStream, verboseStream); + // selecting OpenAsip OR CV-X || ROCC generator + if ((options.CVXIFCoproGen) || (options.roccGen)) { + // Removing FUs in the Core + removeFUsIDF(idf_); + coprogenerator_.coproGenerate( + options, *machine_, *idf_, *plugin_, imemWidthInMAUs, + errorStream, warningStream, verboseStream); + } else { + generator_.generateProcessor( + options, *machine_, *idf_, *plugin_, imemWidthInMAUs, + errorStream, warningStream, verboseStream); + } } catch (Exception& e) { std::cerr << e.errorMessage() << std::endl; } @@ -381,17 +400,16 @@ ProGeUI::generateTestBench( checkIfNull(idf_, "IDF not loaded"); try { - TestBenchBlock coreTestbench( - generator_.generatorContext(), - generator_.processorTopLevel()); - coreTestbench.write(Path(progeOutDir), language); - } catch (Exception& e) { - // There is one case the new test bench can not handle: same data - // address spaces shared by two LSUs. - ProGeTestBenchGenerator tbGen = ProGeTestBenchGenerator(); - tbGen.generate( - language, *machine_, *idf_, dstDir, progeOutDir, entityName_); - } + TestBenchBlock coreTestbench( + generator_.generatorContext(), generator_.processorTopLevel()); + coreTestbench.write(Path(progeOutDir), language); + } catch (Exception& e) { + // There is one case the new test bench can not handle: same data + // address spaces shared by two LSUs. + ProGeTestBenchGenerator tbGen = ProGeTestBenchGenerator(); + tbGen.generate( + language, *machine_, *idf_, dstDir, progeOutDir, entityName_); + } } /** @@ -551,11 +569,13 @@ ProGeUI::generateIDF( std::vector dagops = ProGeTools::generateableDAGOperations(infos, verbose); - auto already_handled = [&](const std::string& name, - const std::vector handledEntries) { - return std::find(handledEntries.begin(), handledEntries.end(), name) != - handledEntries.end(); - }; + auto already_handled = + [&](const std::string& name, + const std::vector handledEntries) { + return std::find( + handledEntries.begin(), handledEntries.end(), name) != + handledEntries.end(); + }; auto select_fu_hdb_implementations = [&]() { for (auto&& fu : machine_->functionUnitNavigator()) { @@ -626,8 +646,7 @@ ProGeUI::generateIDF( if (already_handled(rf->name(), handledRFs)) { continue; } - verbose << " generate implementation for " << rf->name() - << "... "; + verbose << " generate implementation for " << rf->name() << "... "; IDF::RFGenerated rfg(rf->name()); // Assume that we can always generate the missing RFs. verbose << "OK\n"; diff --git a/openasip/src/applibs/ProGe/ProGeUI.hh b/openasip/src/applibs/ProGe/ProGeUI.hh index 68f783ee6..9ee3b3b3d 100644 --- a/openasip/src/applibs/ProGe/ProGeUI.hh +++ b/openasip/src/applibs/ProGe/ProGeUI.hh @@ -38,15 +38,15 @@ #include -#include "TCEString.hh" -#include "ProcessorGenerator.hh" -#include "ProGeTypes.hh" -#include "ICDecoderGeneratorPlugin.hh" +#include "CoproGen.hh" //For CVXIF coprocessor #include "Exception.hh" -#include "PluginTools.hh" +#include "ICDecoderGeneratorPlugin.hh" #include "MemoryGenerator.hh" +#include "PluginTools.hh" #include "ProGeOptions.hh" - +#include "ProGeTypes.hh" +#include "ProcessorGenerator.hh" +#include "TCEString.hh" namespace TTAMachine { class Machine; @@ -116,6 +116,8 @@ private: void generateIDF(const ProGeOptions& options, std::ostream& verboseStream); + void removeFUsIDF(IDF::MachineImplementation* idf); + /// The loaded binary encoding map. BinaryEncoding* bem_; /// The loaded machine implementation. @@ -130,6 +132,8 @@ private: std::string entityName_; ProcessorGenerator generator_; + // CoproGen obj + CoproGen coprogenerator_; static const std::string DEFAULT_ENTITY_STR; }; diff --git a/openasip/src/applibs/ProGe/VerilogNetlistWriter.cc b/openasip/src/applibs/ProGe/VerilogNetlistWriter.cc index 6c32fc6b9..9c07867b3 100644 --- a/openasip/src/applibs/ProGe/VerilogNetlistWriter.cc +++ b/openasip/src/applibs/ProGe/VerilogNetlistWriter.cc @@ -70,6 +70,15 @@ VerilogNetlistWriter::VerilogNetlistWriter( const BaseNetlistBlock& targetBlock) : NetlistWriter(targetBlock), groundWidth_(0) {} +/** + * Overloaded constructor for SV + * + * @param netlist The input netlist. + */ +VerilogNetlistWriter::VerilogNetlistWriter( + const BaseNetlistBlock& targetBlock, HDL lang) + : NetlistWriter(targetBlock), groundWidth_(0), lang_(lang) {} + /** * The destructor. */ @@ -90,7 +99,7 @@ VerilogNetlistWriter::write(const std::string& dstDirectory) { string errorMsg = "Empty input netlist block."; throw InvalidData(__FILE__, __LINE__, __func__, errorMsg); } - + formatresolve(); writeNetlistParameterPackage(dstDirectory); writeBlock(block, dstDirectory); } @@ -104,22 +113,23 @@ VerilogNetlistWriter::write(const std::string& dstDirectory) { void VerilogNetlistWriter::writeNetlistParameterPackage( const std::string& dstDirectory) const { - string fileName = dstDirectory + FileSystem::DIRECTORY_SEPARATOR + - netlistParameterPkgName() + "_pkg.vh"; + string fileName = dstDirectory + FileSystem::DIRECTORY_SEPARATOR + + netlistParameterPkgName() + "_pkg" + format_; ofstream outFile; outFile.open(fileName.c_str(), ofstream::out); if (targetNetlistBlock().netlist().parameterCount() == 0) { - outFile << "parameter " << targetNetlistBlock().moduleName() - << "_DUMMY " << " = 0" << endl; + outFile << "parameter " << targetNetlistBlock().moduleName() + << "_DUMMY " + << " = 0" << endl; } else { - for (size_t i = 0; i < targetNetlistBlock().netlist().parameterCount(); - i++) { + for (size_t i = 0; + i < targetNetlistBlock().netlist().parameterCount(); i++) { Parameter param = targetNetlistBlock().netlist().parameter(i); outFile << "parameter " << param.name() << " = " << param.value(); if (i != targetNetlistBlock().netlist().parameterCount() - 1) outFile << ","; outFile << endl; - } + } } } @@ -145,7 +155,7 @@ void VerilogNetlistWriter::writeBlock( const BaseNetlistBlock& block, const std::string& dstDirectory) { string fileName = dstDirectory + FileSystem::DIRECTORY_SEPARATOR + - block.moduleName() + ".v"; + block.moduleName() + ".v";//format_; if (!FileSystem::fileIsCreatable(fileName) && !(FileSystem::fileExists(fileName) && FileSystem::fileIsWritable(fileName))) { @@ -166,14 +176,15 @@ VerilogNetlistWriter::writeBlock( string separator; outFile << "#(" << endl; if (block.netlist().parameterCount() > 0) { - outFile << "`include \"" << netlistParameterPkgName() << "_pkg.vh\"" - << endl; + outFile << "`include \"" << netlistParameterPkgName() << "_pkg" + << format_ << "\"" << endl; separator = ","; } for (size_t i = 0; i < block.packageCount(); i++) { outFile << separator << endl; - outFile << "`include \"" << block.package(i) << "_pkg.vh\"" << endl; + outFile << "`include \"" << block.package(i) << "_pkg" << format_ + << "\"" << endl; separator = ","; } @@ -210,7 +221,8 @@ VerilogNetlistWriter::writeGenericDeclaration( const BaseNetlistBlock& block, unsigned int indentationLevel, const std::string& indentation, std::ostream& stream) { if (block.parameterCount() > 0) { - if (block.netlist().parameterCount() > 0 || block.packageCount() != 0) { + if (block.netlist().parameterCount() > 0 || + block.packageCount() != 0) { stream << ","; } stream << endl; @@ -228,8 +240,11 @@ VerilogNetlistWriter::writeGenericDeclaration( } else if (param.defaultValue() == "true") { stream << "1"; } else { - string errorMsg = "VerilogNetlistWriter: invalid value for boolean parameter"; - throw InvalidData(__FILE__, __LINE__, __func__, errorMsg); + string errorMsg = + "VerilogNetlistWriter: invalid value for boolean " + "parameter"; + throw InvalidData( + __FILE__, __LINE__, __func__, errorMsg); } } else if (param.type().lower() == PARAM_STRING) { // string literal needs quot. marks @@ -241,7 +256,7 @@ VerilogNetlistWriter::writeGenericDeclaration( stream << param.defaultValue(); } } - if (i != block.parameterCount()-1) { + if (i != block.parameterCount() - 1) { stream << "," << endl; } } @@ -529,8 +544,11 @@ VerilogNetlistWriter::writePortMappings( } else if (param.defaultValue() == "true") { stream << "1"; } else { - string errorMsg = "VerilogNetlistWriter: invalid value for boolean parameter"; - throw InvalidData(__FILE__, __LINE__, __func__, errorMsg); + string errorMsg = + "VerilogNetlistWriter: invalid value for boolean " + "parameter"; + throw InvalidData( + __FILE__, __LINE__, __func__, errorMsg); } } else if (param.type().lower() == PARAM_STRING) { stream << genericMapStringValue(param.value()); @@ -571,17 +589,14 @@ VerilogNetlistWriter::writePortMappings( if (!property.fullyConnected() && dstPort->dataType() == BIT_VECTOR && port.dataType() == BIT) { - index = property.port2FirstBit(); } if (port.dataType() == BIT) { - assert(dstPort->dataType() == BIT_VECTOR); - dstConn = dstPort->name() + "[" + - Conversion::toString(index) + "]"; + dstConn = dstPort->name() + "[" + + Conversion::toString(index) + "]"; } else { - assert(dstPort->dataType() == BIT); if (port.widthFormula() == "1") { srcConn += "[0]"; @@ -591,24 +606,19 @@ VerilogNetlistWriter::writePortMappings( } } } else { - if ((!property.fullyConnected() || dstPort->direction() == OUT) && boost::out_degree( vertexDescriptor, block.netlist()) > 1) { - dstConn = portSignalName(port); } else { - dstConn = dstPort->name(); } } } else { - dstConn = portSignalName(port); } } else { - dstConn = portSignalName(port); } stream << indentation(3) << "." << srcConn << "(" << dstConn << ")"; @@ -730,15 +740,14 @@ VerilogNetlistWriter::portSignalType(const NetlistPort& port) { } else { if (port.realWidthAvailable()) { int width = port.realWidth(); - return " [" + Conversion::toString(width?width-1:0) + ":0]"; + return " [" + Conversion::toString(width ? width - 1 : 0) + ":0]"; } else if (isNumber(port.widthFormula()) && (Conversion::toInt(port.widthFormula()) == 0)) { return " [0:0]"; } else if (usesParameterWidth(port)) { - return " [" + parameterWidthValue(port) + - "-1 :0]"; + return " [" + parameterWidthValue(port) + "-1 :0]"; } else { - return " [ " + port.widthFormula()+"-1: 0]"; + return " [ " + port.widthFormula() + "-1: 0]"; } } } @@ -770,5 +779,13 @@ VerilogNetlistWriter::parameterWidthValue(const NetlistPort& port) { return port.parentBlock().parameter(port.widthFormula()).value(); } - +// Outputs the format as a string .v or .vh +void +VerilogNetlistWriter::formatresolve() { + if (lang_ == SV) { + format_ = ".sv"; + } else { + format_ = ".vh"; + } +} } diff --git a/openasip/src/applibs/ProGe/VerilogNetlistWriter.hh b/openasip/src/applibs/ProGe/VerilogNetlistWriter.hh index 37334752a..2f0ab28d5 100644 --- a/openasip/src/applibs/ProGe/VerilogNetlistWriter.hh +++ b/openasip/src/applibs/ProGe/VerilogNetlistWriter.hh @@ -52,6 +52,9 @@ class BaseNetlistBlock; class VerilogNetlistWriter : public NetlistWriter { public: VerilogNetlistWriter(const BaseNetlistBlock& targetBlock); + VerilogNetlistWriter( + const BaseNetlistBlock& targetBlock, + HDL lang); // constructor included with SV language virtual ~VerilogNetlistWriter(); virtual void write(const std::string& dstDirectory); @@ -114,10 +117,14 @@ private: static std::string portSignalName(const NetlistPort& port); static std::string portSignalType(const NetlistPort& port); static TCEString parameterWidthValue(const NetlistPort& port); - + void formatresolve(); + /// Width of the ground signal. int groundWidth_; - + // For Language: Verilog OR SV + HDL lang_; + // Format SV or v + std::string format_; }; } diff --git a/openasip/src/applibs/bem/BEMGenerator.cc b/openasip/src/applibs/bem/BEMGenerator.cc index da161cf55..3eb1df0e7 100644 --- a/openasip/src/applibs/bem/BEMGenerator.cc +++ b/openasip/src/applibs/bem/BEMGenerator.cc @@ -87,6 +87,15 @@ BEMGenerator::BEMGenerator(const Machine& machine) : machine_(&machine) { } +/** + * BEM constructor for the Coprocessor generation. + * + * @param machine The machine for which the binary encoding map will be + * generated. + * @param coproInterF Bool for enabling the ROCC format + */ +BEMGenerator::BEMGenerator(const Machine& machine, bool coproInterF) + : machine_(&machine), rocc_(coproInterF) {} /** * The destructor. @@ -192,6 +201,41 @@ BEMGenerator::addTopLevelFields(BinaryEncoding& bem) const { } } +/** + * RISC-V F7,F3 encoding generation + */ + +void +BEMGenerator::funcencodeRiscv( + OperationTriggeredFormat* format, InstructionFormat* instrFormat, + const unsigned& custom_op, unsigned& amountOfRCustomOps, + unsigned& rocc_f3) const { + for (int i = 0; i < format->operationCount(); i++) { + const std::string op = format->operationAtIndex(i); + if (MapTools::containsKey(RISCVFields::RISCVRTypeOperations, op)) { + instrFormat->addOperation( + op, RISCVFields::RISCVRTypeOperations.at(op)); + } else { + if (rocc_) { // For the ROCC interface F3=rocc_f3, F7 in + // incrementals + unsigned int customEncoding = (rocc_f3 << 7) + custom_op; + customEncoding += (amountOfRCustomOps << 10); + amountOfRCustomOps++; + // 10 bit encoding for operation + assert(amountOfRCustomOps < 1024); + instrFormat->addOperation(op, customEncoding); + } else { // F7:F3 in incrementals + unsigned int customEncoding = custom_op; + customEncoding += (amountOfRCustomOps << 7); + amountOfRCustomOps++; + // 10 bit encoding for operation + assert(amountOfRCustomOps < 1024); + instrFormat->addOperation(op, customEncoding); + } + } + } +} + /** * Adds a RISC-V format to the binary encoding map * @@ -207,6 +251,8 @@ BEMGenerator::addRiscvFormat( InstructionFormat* instrFormat = new InstructionFormat(name, bem); const unsigned OPC_CUSTOM_0 = 0b0001011; const unsigned OPC_CUSTOM_1 = 0b0101011; + unsigned OP_ROCC_F3 = + 0b0; // For ROCC F3 enconding for R type custom instructions if (name == RISCVFields::RISCV_R_TYPE_NAME) { OperationTriggeredEncoding* rs1 = @@ -223,21 +269,12 @@ BEMGenerator::addRiscvFormat( new OperationTriggeredField(*opcode, 0, 0, 7); new OperationTriggeredField(*opcode, 1, 12, 3); new OperationTriggeredField(*opcode, 2, 25, 7); + OP_ROCC_F3 = 0b111; + // Encoding generation + funcencodeRiscv( + format, instrFormat, OPC_CUSTOM_0, amountOfR3RCustomOps, + OP_ROCC_F3); - // Reserve first few encodings for fixed special case - for (int i = 0; i < format->operationCount(); i++) { - const std::string op = format->operationAtIndex(i); - if (MapTools::containsKey(RISCVFields::RISCVRTypeOperations, op)) { - instrFormat->addOperation(op, RISCVFields::RISCVRTypeOperations.at(op)); - } else { - unsigned int customEncoding = OPC_CUSTOM_0; - customEncoding += (amountOfRCustomOps << 7); - amountOfRCustomOps++; - // 10 bit encoding for operation - assert(amountOfRCustomOps < 1024); - instrFormat->addOperation(op, customEncoding); - } - } } else if (name == RISCVFields::RISCV_I_TYPE_NAME) { // TODO: shift operations use immediate bits for funct code in this // format @@ -364,7 +401,12 @@ BEMGenerator::addRiscvFormat( new OperationTriggeredField(*opcode, 0, 0, 7); new OperationTriggeredField(*opcode, 1, 12, 3); new OperationTriggeredField(*opcode, 2, 25, 7); + OP_ROCC_F3 = 0b110; + funcencodeRiscv( + format, instrFormat, OPC_CUSTOM_0, amountOfR3RCustomOps, + OP_ROCC_F3); + /* for (int i = 0; i < format->operationCount(); i++) { const std::string op = format->operationAtIndex(i); unsigned int customEncoding = OPC_CUSTOM_0; @@ -373,7 +415,7 @@ BEMGenerator::addRiscvFormat( // 10 bit encoding for operation assert(amountOfRCustomOps < 1024); instrFormat->addOperation(op, customEncoding); - } + }*/ // unary without output, stdout for example } else if (name == RISCVFields::RISCV_R1_TYPE_NAME) { OperationTriggeredEncoding* rs1 = diff --git a/openasip/src/applibs/bem/BEMGenerator.hh b/openasip/src/applibs/bem/BEMGenerator.hh index d9502de7c..6e28c7a33 100644 --- a/openasip/src/applibs/bem/BEMGenerator.hh +++ b/openasip/src/applibs/bem/BEMGenerator.hh @@ -45,6 +45,7 @@ class DestinationField; class SourceField; class GuardField; class SocketCodeTable; +class InstructionFormat; namespace TTAMachine { class Machine; @@ -61,6 +62,7 @@ namespace TTAMachine { class BEMGenerator { public: BEMGenerator(const TTAMachine::Machine& machine); + BEMGenerator(const TTAMachine::Machine& machine, bool coproInterF); virtual ~BEMGenerator(); BinaryEncoding* generate(); @@ -79,6 +81,10 @@ private: void addEncodings(DestinationField& field) const; void addEncodings(SourceField& field) const; void addEncodings(GuardField& field) const; + void funcencodeRiscv( + TTAMachine::OperationTriggeredFormat* format, + InstructionFormat* instrFormat, const unsigned& custom_op, + unsigned& amountOfRCustomOps, unsigned& rocc_f3) const; void addRiscvFormat(TTAMachine::OperationTriggeredFormat* format, BinaryEncoding& bem, unsigned& amountOfRCustomOps, @@ -129,6 +135,8 @@ private: const TTAMachine::Machine* machine_; /// A map which tells which socket code table belongs to a socket. SCTableMap scTableMap_; + // ROCC interface selector for the Coprocessor Generation + bool rocc_; }; #endif diff --git a/openasip/src/bintools/Compiler/oacc-riscv.in b/openasip/src/bintools/Compiler/oacc-riscv.in index 84d8c072c..126b2515e 100755 --- a/openasip/src/bintools/Compiler/oacc-riscv.in +++ b/openasip/src/bintools/Compiler/oacc-riscv.in @@ -169,13 +169,18 @@ def move_file(src, dst): def getLLVMTargetPlugin(): + if options.rocc_enable: + rocc_encode = "T" + else: + rocc_encode = "F" + if runningInstalled: riscvtdgen = "riscv-tdgen" else: riscvtdgen = "@abs_top_builddir@" + "/src/bintools/Compiler/riscv-tdgen/riscv-tdgen" exitCode = run_command( - riscvtdgen + " -o " + tempDir + " -a " + options.adf, + riscvtdgen + " -o " + tempDir + " -a " + options.adf + " -r " + rocc_encode, echoStdout=options.verbose, echoCmd=options.verbose) @@ -368,6 +373,10 @@ def parse_options(): dest="clear_plugin_cache", default=False, help="Clear plugin cache") + parser.add_option("-r", "--rocc", dest="rocc_enable", + action="store_true", default=False, + help="Enable ROCC interface compatible custom instruction generation") + # fix gcc switches, which cannot be represented in OptionParser format by adding extra "-" before switch @@ -477,6 +486,10 @@ def compile_to_bitcode(input_files): else: tceopgen = "@abs_top_builddir@" + "/src/bintools/Compiler/tceopgen/tceopgen" + if options.rocc_enable: + options.march = "riscv64" + compileFlags += "-march=rv64gc -mcmodel=medany " + exitCode = run_command( tceopgen + " -o " + tceOpsPath, echoCmd=options.verbose) diff --git a/openasip/src/bintools/Compiler/riscv-tdgen/riscv-tdgen.cc b/openasip/src/bintools/Compiler/riscv-tdgen/riscv-tdgen.cc index 6f340be91..23f4600e5 100644 --- a/openasip/src/bintools/Compiler/riscv-tdgen/riscv-tdgen.cc +++ b/openasip/src/bintools/Compiler/riscv-tdgen/riscv-tdgen.cc @@ -38,12 +38,15 @@ int main(int argc, char* argv[]) { std::string outputDir; std::string adfPath; + std::string rocc_str; + bool rocc; // Check if the correct number of arguments is provided - if (argc != 5) { + if (!(argc == 7 || argc == 5)) { std::cout << "Usage: riscv-tdgen" << std::endl << " -o Output directory." << std::endl - << " -a ADF path." << std::endl; + << " -a ADF path." << std::endl + << " -r 'T':Enable ROCC encodings, OR 'F'. Default:F" << std::endl; return EXIT_FAILURE; } @@ -53,13 +56,21 @@ int main(int argc, char* argv[]) { outputDir = argv[i + 1]; } else if (std::strcmp(argv[i], "-a") == 0) { adfPath = argv[i + 1]; + } else if (std::strcmp(argv[i], "-r") == 0) { + rocc_str = argv[i + 1]; } } TTAMachine::Machine* mach = TTAMachine::Machine::loadFromADF(adfPath); assert(mach != NULL); - RISCVTDGen tdgen(*mach); + if (rocc_str == "T") { + rocc = true; + } else { + rocc = false; + } + + RISCVTDGen tdgen(*mach, rocc); tdgen.generateBackend(outputDir); delete mach; diff --git a/openasip/src/procgen/ProGe/CoprocessorCmdLineOptions.cc b/openasip/src/procgen/ProGe/CoprocessorCmdLineOptions.cc new file mode 100644 index 000000000..e76b74c88 --- /dev/null +++ b/openasip/src/procgen/ProGe/CoprocessorCmdLineOptions.cc @@ -0,0 +1,213 @@ +/* + Copyright (C) 2025 Tampere University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ +/** + * @file CoprocessorCmdLineOptions.cc + * + * Implementation of CoprocessorCmdLineOptions class from ProGeCmdLineOptions + * + * @author Tharaka Sampath + */ + +#include "CoprocessorCmdLineOptions.hh" + +#include +#include + +#include "tce_config.h" + +using std::cout; +using std::endl; +using std::string; + +const string InterF = "coproInterface"; +const string BEM_PARAM_NAME = "bem"; +const string IDF_PARAM_NAME = "idf"; +const string HDL_PARAM_NAME = "hdl"; +const string OUTPUTDIR_PARAM_NAME = "output"; +const string ENTITY_NAME = "entity-name"; +const string FORCE_OUTPUT = "force-output"; +const string PREFER_GEN = "prefer-generation"; +const string HDB_LIST = "hdb-list"; + +/** + * The constructor. + */ +CoprocessorCmdLineOptions::CoprocessorCmdLineOptions() : CmdLineOptions("") { + StringCmdLineOptionParser* Interface = new StringCmdLineOptionParser( + InterF, "Select the Coprocessor Interface :- 'cvx' OR 'rocc'", "c"); + addOption(Interface); + + StringCmdLineOptionParser* bemFile = + new StringCmdLineOptionParser(BEM_PARAM_NAME, "The BEM file ", "b"); + addOption(bemFile); + + StringCmdLineOptionParser* hdlParam = new StringCmdLineOptionParser( + HDL_PARAM_NAME, "The language of the HDL to generate. 'vhdl' = VHDL", + "l"); + addOption(hdlParam); + + StringCmdLineOptionParser* idfFile = + new StringCmdLineOptionParser(IDF_PARAM_NAME, "The IDF file", "i"); + addOption(idfFile); + + StringCmdLineOptionParser* outputDirectory = + new StringCmdLineOptionParser( + OUTPUTDIR_PARAM_NAME, "The output directory", "o"); + addOption(outputDirectory); + + StringCmdLineOptionParser* entityName = new StringCmdLineOptionParser( + ENTITY_NAME, "Coprocessor TOP name as a String.", "e"); + addOption(entityName); + + BoolCmdLineOptionParser* preferGen = new BoolCmdLineOptionParser( + PREFER_GEN, + "Prefer HDL generation over existing HDB implementations."); + addOption(preferGen); + + StringCmdLineOptionParser* hdbList = new StringCmdLineOptionParser( + HDB_LIST, "Comma separated list of HDBs for automated generation.", + "h"); + addOption(hdbList); +} + +/** + * The destructor. + */ +CoprocessorCmdLineOptions::~CoprocessorCmdLineOptions() {} + +/** + * Returns the ADF or PCF file given as last argument. + * + * @return The name of the file. + */ +std::string +CoprocessorCmdLineOptions::processorToGenerate() const { + return argument(numberOfArguments()); +} + +/** + * Selects the Interface for the Coprocessor + */ +std::string +CoprocessorCmdLineOptions::interFace() const { + return findOption(InterF)->String(); +} + +/** + * Returns the given BEM file. + * + * @return The name of the file. + */ +std::string +CoprocessorCmdLineOptions::bemFile() const { + return findOption(BEM_PARAM_NAME)->String(); +} + +/** + * Returns the given IDF file. + * + * @return The name of the file. + */ +std::string +CoprocessorCmdLineOptions::idfFile() const { + return findOption(IDF_PARAM_NAME)->String(); +} + +/** + * Returns the given HDL parameter. + * + * @return The HDL parameter. + */ +std::string +CoprocessorCmdLineOptions::hdl() const { + return findOption(HDL_PARAM_NAME)->String(); +} + +/** + * Returns the given output directory. + * + * @return The given output directory. + */ +std::string +CoprocessorCmdLineOptions::outputDirectory() const { + return findOption(OUTPUTDIR_PARAM_NAME)->String(); +} + +std::string +CoprocessorCmdLineOptions::entityName() const { + return findOption(ENTITY_NAME)->String(); +} + +/** + * Returns true if preferring HDL Generation. + */ +bool +CoprocessorCmdLineOptions::preferHDLGeneration() const { + return findOption(PREFER_GEN)->isFlagOn(); +} + +/** + * Helper for arguments with comma-separated arguments + */ +std::vector +CoprocessorCmdLineOptions::commaSeparatedList( + const std::string argumentName) const { + std::vector list; + std::string str; + if (findOption(argumentName)->isDefined()) { + str = findOption(argumentName)->String(); + } + std::stringstream ss(str); + while (ss.good()) { + std::string sub; + std::getline(ss, sub, ','); + if (sub.size() > 1) { + list.emplace_back(sub); + } + } + return list; +} + +/** + * Return list of HDBs. + */ +std::vector +CoprocessorCmdLineOptions::hdbList() const { + return commaSeparatedList(HDB_LIST); +} + +/** + * Prints the version of the application. + */ +void +CoprocessorCmdLineOptions::printVersion() const { + std::cout << "generatecoprocessor - coprocessor generator " + << Application::TCEVersionString() << std::endl; +} + +/** + * Prints help of the application. + */ +void +CoprocessorCmdLineOptions::printHelp() const { + printVersion(); + cout << "Usage: generatecoprocessor [options] " << endl + << "where means an ADF file." << endl; + CmdLineOptions::printHelp(); +} diff --git a/openasip/src/procgen/ProGe/CoprocessorCmdLineOptions.hh b/openasip/src/procgen/ProGe/CoprocessorCmdLineOptions.hh new file mode 100644 index 000000000..a7e4a3fee --- /dev/null +++ b/openasip/src/procgen/ProGe/CoprocessorCmdLineOptions.hh @@ -0,0 +1,63 @@ +/* + Copyright (C) 2025 Tampere University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ +/** + * @file CoprocessorCmdLineOptions.hh + * + * Declaration of CoprocessorCmdLineOptions class from ProGeCmdLineOptions. + * + * @author Tharaka Sampath + */ + +#ifndef COPRO_CMD_LINE_OPTIONS_HH +#define COPRO_CMD_LINE_OPTIONS_HH + +#include // std::pair + +#include "CmdLineOptions.hh" +/** + * Command line options for the command line interface of ProGe + * (generateprocessor). + */ +class CoprocessorCmdLineOptions : public CmdLineOptions { +public: + CoprocessorCmdLineOptions(); + virtual ~CoprocessorCmdLineOptions(); + + std::string cusOpcode() const; + std::string processorToGenerate() const; + std::string bemFile() const; + std::string idfFile() const; + std::string hdl() const; + std::string outputDirectory() const; + + std::string entityName() const; + bool forceOutputDirectory() const; + bool preferHDLGeneration() const; + std::vector commaSeparatedList( + const std::string argumentName) const; + std::vector hdbList() const; + + // Interface parameters + std::string interFace() const; + + virtual void printVersion() const; + virtual void printHelp() const; +}; + +#endif diff --git a/openasip/src/procgen/ProGe/GenerateCoprocessor.cc b/openasip/src/procgen/ProGe/GenerateCoprocessor.cc new file mode 100644 index 000000000..aa98e65f2 --- /dev/null +++ b/openasip/src/procgen/ProGe/GenerateCoprocessor.cc @@ -0,0 +1,164 @@ +/* + Copyright (C) 2025 Tampere University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ +/** + * @file GenerateCoprocessor.cc + * + * Implementation of GenerateCoprocessor class from GenerateProcessor. + * + * @author Tharaka Sampath + */ + +#include "GenerateCoprocessor.hh" + +#include +#include + +#include "BinaryEncoding.hh" +#include "CoprocessorCmdLineOptions.hh" +#include "Environment.hh" +#include "FileSystem.hh" +#include "Machine.hh" +#include "StringTools.hh" + +using namespace ProGe; +using std::cerr; +using std::cout; +using std::endl; +using std::string; +using std::vector; + +/** + * The main program of generatecoprocessor application. + */ +int +main(int argc, char* argv[]) { + GenerateCoprocessor ui; + bool successful = ui.generateCoprocessor(argc, argv); + if (successful) { + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } +} + +/** + * The constructor. + */ +GenerateCoprocessor::GenerateCoprocessor() {} + +/** + * The destructor. + */ +GenerateCoprocessor::~GenerateCoprocessor() {} + +/** + * Parses the command line arguments and generates the coprocessor with FUs. + * + * @return True if the generation of the coprocessor was succesful, otherwise + * false. + */ +bool +GenerateCoprocessor::generateCoprocessor(int argc, char* argv[]) { + CoprocessorCmdLineOptions options; + string entity = ""; + + try { + options.parse(argv, argc); + ProGeOptions progeOptions(options, true); + + if (options.isVerboseSwitchDefined()) { + Application::setVerboseLevel( + Application::VERBOSE_LEVEL_INCREASED); + } + + if (options.isVerboseSpamSwitchDefined()) { + Application::setVerboseLevel(Application::VERBOSE_LEVEL_SPAM); + } + + if (options.numberOfArguments() == 0) { + options.printHelp(); + return false; + } + + if (FileSystem::fileExists(progeOptions.outputDirectory)) { + cerr << "Error: Output directory " << progeOptions.outputDirectory + << " already exists." << endl; + return false; + } + + string processorDefinition = options.processorToGenerate(); + if (FileSystem::fileExtension(processorDefinition) == ".adf") { + loadMachine(processorDefinition); + } else if (FileSystem::fileExtension(processorDefinition) == ".pcf") { + loadProcessorConfiguration(processorDefinition); + } else { + cerr << "Unknown file: " << processorDefinition + << ". The given file must be either an ADF or PCF file." + << endl; + throw IllegalCommandLine(__FILE__, __LINE__, __func__); + } + + string bem = options.bemFile(); + string idf = options.idfFile(); + string hdl = options.hdl(); + + int imemWidthInMAUs = 4; + if (idf != "") { + loadMachineImplementation(idf); + } + + ProGeUI::generateProcessor( + progeOptions, imemWidthInMAUs, std::cerr, std::cerr, std::cerr); + } catch (ParserStopRequest const&) { + return false; + } catch (const IllegalCommandLine& exception) { + cerr << exception.errorMessage() << endl; + return false; + } catch (const Exception& e) { + cerr << e.errorMessage() << endl; + cerr << "Exception thrown at: " << e.fileName() << ":" << e.lineNum() + << endl; + cerr << " message: " << e.errorMessage() << endl; + return false; + } + + ProGeOptions progeOptions(options, true); + + return true; +} + +/** + * Generates the output directory name. + * + * @param options Proge command line options. + * @param outputDir String where output directory name is to be stored. + */ +void +GenerateCoprocessor::getOutputDir( + const CoprocessorCmdLineOptions& options, std::string& outputDir) { + outputDir = options.outputDirectory(); + + if (outputDir == "") { + outputDir = FileSystem::currentWorkingDir() + + FileSystem::DIRECTORY_SEPARATOR + "_out"; + } else { + outputDir = FileSystem::expandTilde(outputDir); + outputDir = FileSystem::absolutePathOf(outputDir); + } +} diff --git a/openasip/src/procgen/ProGe/GenerateCoprocessor.hh b/openasip/src/procgen/ProGe/GenerateCoprocessor.hh new file mode 100644 index 000000000..d50a2b2ae --- /dev/null +++ b/openasip/src/procgen/ProGe/GenerateCoprocessor.hh @@ -0,0 +1,50 @@ +/* + Copyright (C) 2025 Tampere University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ +/** + * @file GenerateCoprocessor.hh + * + * Declaration of GenerateCoprocessor class from GenerateProcessor. + * + * @author Tharaka Sampath + */ + +#ifndef GENERATE_COPROCESSOR_HH +#define GENERATE_COPROCESSOR_HH + +#include "Exception.hh" +#include "ProGeUI.hh" + +class CoprocessorCmdLineOptions; + +/** + * Implements the command line user interface 'generateCoprocessor'. + */ +class GenerateCoprocessor : public ProGe::ProGeUI { +public: + GenerateCoprocessor(); + virtual ~GenerateCoprocessor(); + + bool generateCoprocessor(int argc, char* argv[]); + +private: + void getOutputDir( + const CoprocessorCmdLineOptions& options, std::string& outputDir); +}; + +#endif diff --git a/openasip/src/procgen/ProGe/Makefile.am b/openasip/src/procgen/ProGe/Makefile.am index 0f60fe301..7382e31d9 100644 --- a/openasip/src/procgen/ProGe/Makefile.am +++ b/openasip/src/procgen/ProGe/Makefile.am @@ -31,7 +31,7 @@ LIB_BEM_DIR = ../../base/bem APPLIBS_FSA_DIR = ../../applibs/FSA bin_SCRIPTS = buildicdecoderplugin -bin_PROGRAMS = generateprocessor ttaunittester +bin_PROGRAMS = generateprocessor ttaunittester generatecoprocessor generateprocessor_SOURCES = GenerateProcessor.cc \ ProGeCmdLineOptions.cc ProGeCmdLineOptions.hh GenerateProcessor.hh @@ -43,6 +43,11 @@ TTAUnitTesterCmdLineOptions.hh TTAUnitTester.hh ttaunittester_LDADD = ../../libopenasip.la +generatecoprocessor_SOURCES = GenerateCoprocessor.cc \ +CoprocessorCmdLineOptions.cc CoprocessorCmdLineOptions.hh GenerateCoprocessor.hh + +generatecoprocessor_LDADD = ../../libopenasip.la + AM_CPPFLAGS = -I${HDB_DIR} -I${TOOLS_DIR} -I${PROGE_APPLIBS_DIR} \ -I${MACH_DIR} -I${BEM_DIR} -I${IMPL_TESTER_DIR} -I${IDF_DIR} \ -I${IDF_APPLIBS_DIR} -I${SIM_APPLIB_DIR} -I${OSAL_DIR} \ @@ -64,4 +69,8 @@ MAINTAINERCLEANFILES = *~ *.gcov *.bbg *.bb *.da generateprocessor_SOURCES += \ TTAUnitTesterCmdLineOptions.hh GenerateProcessor.hh \ ProGeCmdLineOptions.hh TTAUnitTester.hh + +generatecoprocessor_SOURCES += \ + TTAUnitTesterCmdLineOptions.hh GenerateCoprocessor.hh \ + CoprocessorCmdLineOptions.hh TTAUnitTester.hh ## headers end diff --git a/openasip/src/procgen/ProGe/ProGeCmdLineOptions.cc b/openasip/src/procgen/ProGe/ProGeCmdLineOptions.cc index 7bc0b0d47..59b11171c 100644 --- a/openasip/src/procgen/ProGe/ProGeCmdLineOptions.cc +++ b/openasip/src/procgen/ProGe/ProGeCmdLineOptions.cc @@ -244,7 +244,6 @@ ProGeCmdLineOptions::ProGeCmdLineOptions() : "Initialize FUGen generated signals as don't care. " "E.g. some FPGA tool optimizations prefer these."); addOption(dontCareInit); - } @@ -551,7 +550,6 @@ ProGeCmdLineOptions::dontCareInitialization() const { return findOption(DONT_CARE_INIT)->isFlagOn(); } - /** * Prints the version of the application. */ diff --git a/openasip/src/procgen/ProGe/ProGeCmdLineOptions.hh b/openasip/src/procgen/ProGe/ProGeCmdLineOptions.hh index ee4605632..932d8b111 100644 --- a/openasip/src/procgen/ProGe/ProGeCmdLineOptions.hh +++ b/openasip/src/procgen/ProGe/ProGeCmdLineOptions.hh @@ -37,7 +37,7 @@ #include // std::pair #include "CmdLineOptions.hh" /** - * Command line options for the command line interface of ProGe + * Command line options for the command line interface of ProGe * (generateprocessor). */ class ProGeCmdLineOptions : public CmdLineOptions { diff --git a/openasip/src/tools/RISCVTools.hh b/openasip/src/tools/RISCVTools.hh index 84c0f30d8..6e5bce8d0 100644 --- a/openasip/src/tools/RISCVTools.hh +++ b/openasip/src/tools/RISCVTools.hh @@ -34,14 +34,16 @@ class InstructionFormat; class RISCVTools { public: - static std::string getFunc3Str(const int encoding); - static std::string getFunc7Str(const int encoding); - static std::string getFunc2Str(const int encoding); - static std::string getOpcodeStr(const int encoding); - static int getFunc3Int(const int encoding); - static int getFunc7Int(const int encoding); - static int getFunc2Int(const int encoding); - static int getOpcodeInt(const int encoding); + static inline std::string getFunc3Str(const int encoding); + static inline std::string getFunc7Str(const int encoding); + static inline std::string getFunc2Str(const int encoding); + static inline std::string getOpcodeStr(const int encoding); + static inline int getFunc3Int(const int encoding); + static inline int getFunc7Int(const int encoding); + static inline int getFunc2Int(const int encoding); + static inline int getOpcodeInt(const int encoding); + static inline void findCustomOps( + std::map& customOps_, BinaryEncoding* bem_); }; #include "RISCVTools.icc" diff --git a/openasip/src/tools/RISCVTools.icc b/openasip/src/tools/RISCVTools.icc index 6d03f3638..f2a7e9136 100644 --- a/openasip/src/tools/RISCVTools.icc +++ b/openasip/src/tools/RISCVTools.icc @@ -86,3 +86,26 @@ RISCVTools::getOpcodeStr(const int encoding) { assert(encStr.length() == 7); return "0b" + encStr; } + +void +RISCVTools::findCustomOps(std::map& customOps_, BinaryEncoding* bem_) { + customOps_.clear(); + const std::vector formatsToSearch = { + RISCVFields::RISCV_R_TYPE_NAME, + RISCVFields::RISCV_R1R_TYPE_NAME, + RISCVFields::RISCV_R1_TYPE_NAME, + RISCVFields::RISCV_R3R_TYPE_NAME + }; + for (const std::string& fName : formatsToSearch) { + InstructionFormat* format = bem_->instructionFormat(fName); + if (format == NULL) { + continue; + } + for (int i = 0; i < format->operationCount(); i++) { + const std::string op = format->operationAtIndex(i); + if (!MapTools::containsKey(RISCVFields::RISCVRTypeOperations, op)) { + customOps_.insert({op, format->encoding(op)}); + } + } + } +} \ No newline at end of file diff --git a/testsuite/systemtest/procgen/ProGe/coprocessor_gen_test.sh b/testsuite/systemtest/procgen/ProGe/coprocessor_gen_test.sh new file mode 100755 index 000000000..d91aa98c4 --- /dev/null +++ b/testsuite/systemtest/procgen/ProGe/coprocessor_gen_test.sh @@ -0,0 +1,43 @@ +#!/bin/bash +### TCE TESTCASE +### title: Test for the generation and compilation of the coprocessor files + +DATA=./data +ADF="${DATA}/rv32im_test.adf" +HDB1="${DATA}/valgen.hdb" +OPENASIP_HDB_PATH=",../../../../openasip/hdb" +HDB2="${OPENASIP_HDB_PATH}/generate_base32.hdb" +HDB3="${OPENASIP_HDB_PATH}/generate_lsu_32.hdb" +HDB4="${OPENASIP_HDB_PATH}/generate_rf_iu.hdb" +HDB5="${OPENASIP_HDB_PATH}/asic_130nm_1.5V.hdb" +CVX_OUT="cvx_output" +ROCC_OUT="ROCC_output" + +clear_test_data() { + rm -rf $CVX_OUT + rm -rf $ROCC_OUT +} + +clear_test_data + +generatecoprocessor -c "cvx" --hdb-list ${HDB1}${HDB2}${HDB3}${HDB4}${HDB5} -o ${CVX_OUT} $ADF \ + >& /dev/null|| echo "Error from CV-X-IF Generation" + +generatecoprocessor -c "rocc" --hdb-list ${HDB1}${HDB2}${HDB3}${HDB4}${HDB5} -o ${ROCC_OUT} $ADF \ + >& /dev/null|| echo "Error from ROCC Generation" + +# If verilator is found from PATH, compile and simulate +VERILA=$(which verilator 2> /dev/null) +if [ "x${VERILA}" != "x" ] +then + cd ${ROCC_OUT}/systemverilog || exit 1 + verilator --lint-only fu_custom.sv coprocessor_custom.sv -Wno-WIDTH >& /dev/null|| echo "ROCC compilation failed." + + cd ../../${CVX_OUT}/systemverilog || exit 1 + verilator --lint-only ../../data/cva6_config_pkg.sv cvxif_sup_pkg.sv cvxifcompressed_decoder.sv instr_tracker_custom.sv fu_custom.sv \ + custom_coprocessor.sv ../../data/cva6_top.sv --top-module cva6_top -Wno-WIDTH -Wno-CASEINCOMPLETE -Wno-SYMRSVDWORD \ + >& /dev/null || echo "CV-X-IF compilation failed." + cd ../../ +fi +clear_test_data +exit 0 diff --git a/testsuite/systemtest/procgen/ProGe/data/cva6_config_pkg.sv b/testsuite/systemtest/procgen/ProGe/data/cva6_config_pkg.sv new file mode 100755 index 000000000..9d7c11a13 --- /dev/null +++ b/testsuite/systemtest/procgen/ProGe/data/cva6_config_pkg.sv @@ -0,0 +1,35 @@ +// Copyright (c) 2025 Tampere University. +// +// This file is part of TTA-Based Codesign Environment (TCE). +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +package cva6_config_pkg; + parameter int unsigned CVA6ConfigXlen = 3; + + typedef struct packed{ + int unsigned NrScoreboardEntries; + } cva6_cfg_t; + + localparam cva6_cfg_t cva6_cfg = '{ + NrScoreboardEntries : unsigned'(8) + }; + + +endpackage diff --git a/testsuite/systemtest/procgen/ProGe/data/cva6_top.sv b/testsuite/systemtest/procgen/ProGe/data/cva6_top.sv new file mode 100755 index 000000000..6a63cdf63 --- /dev/null +++ b/testsuite/systemtest/procgen/ProGe/data/cva6_top.sv @@ -0,0 +1,120 @@ +// Copyright (c) 2025 Tampere University. +// +// This file is part of TTA-Based Codesign Environment (TCE). +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +module cva6_top +import cvxif_sup_pkg::*; +( + input logic clk_i, + input logic rst_ni +); + + typedef struct packed { + logic [15:0] instr; + logic [cvxif_sup_pkg::X_ID_WIDTH-1:0] hartid; + } x_compressed_req_t; + + typedef struct packed { + logic [31:0] instr; + logic accept; + } x_compressed_resp_t; + + typedef struct packed { + logic [31:0] instr; + logic [cvxif_sup_pkg::X_ID_WIDTH-1:0] hartid; + logic [cvxif_sup_pkg::X_ID_WIDTH-1:0] id; + } x_issue_req_t; + + typedef struct packed { + logic accept; + logic writeback; + logic [2:0] register_read; + } x_issue_resp_t; + + typedef struct packed { + logic [cvxif_sup_pkg::X_ID_WIDTH-1:0] hartid; + logic [cvxif_sup_pkg::X_ID_WIDTH-1:0] id; + logic commit_kill; + } x_commit_t; + + typedef struct packed { + logic [cvxif_sup_pkg::X_ID_WIDTH-1:0] hartid; + logic [cvxif_sup_pkg::X_ID_WIDTH-1:0] id; + logic [2:0][31:0] rs; + logic [2:0] rs_valid; + } x_register_t; + + typedef struct packed { + logic [cvxif_sup_pkg::X_ID_WIDTH-1:0] hartid; + logic [cvxif_sup_pkg::X_ID_WIDTH-1:0] id; + logic [cvxif_sup_pkg::X_RFW_WIDTH-1:0] data; + logic [4:0] rd; + logic we; + } x_result_t; + + typedef struct packed { + logic compressed_valid; + x_compressed_req_t compressed_req; + logic issue_valid; + x_issue_req_t issue_req; + logic register_valid; + x_register_t register; + logic commit_valid; + x_commit_t commit; + logic result_ready; + } cvxif_req_t; + + typedef struct packed { + logic compressed_ready; + x_compressed_resp_t compressed_resp; + logic issue_ready; + x_issue_resp_t issue_resp; + logic register_ready; + logic result_valid; + x_result_t result; + } cvxif_resp_t; + + cvxif_req_t cvxif_req; + cvxif_resp_t cvxif_resp; + + custom_coprocessor #( + .NrRgprPorts (2), + .readregflags_t (logic [1:0]), + .writeregflags_t (logic), + .id_t (logic [cvxif_sup_pkg::X_ID_WIDTH-1:0]), + .hartid_t (logic [cvxif_sup_pkg::X_ID_WIDTH-1:0]), + .x_compressed_req_t (x_compressed_req_t), + .x_compressed_resp_t (x_compressed_resp_t), + .x_issue_req_t (x_issue_req_t), + .x_issue_resp_t (x_issue_resp_t), + .x_register_t (x_register_t), + .x_commit_t (x_commit_t), + .x_result_t (x_result_t), + .cvxif_req_t (cvxif_req_t), + .cvxif_resp_t (cvxif_resp_t) + ) i_cvxif_coprocessor ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .cvxif_req_i (cvxif_req), + .cvxif_resp_o (cvxif_resp) + ); + +endmodule diff --git a/testsuite/systemtest/procgen/ProGe/data/rv32im_test.adf b/testsuite/systemtest/procgen/ProGe/data/rv32im_test.adf new file mode 100644 index 000000000..1c33482fb --- /dev/null +++ b/testsuite/systemtest/procgen/ProGe/data/rv32im_test.adf @@ -0,0 +1,1302 @@ + + + + + + + 32 + + + + + + + + sign + 0 + + + + + 32 + + + + + + + + sign + 0 + + + + + 32 + + + + + + + + sign + 0 + + + + + 32 + + + + + + + + sign + 1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B3 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B3 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B3 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + ALU_i1 + 32 + + + + + ALU_i2 + 32 + + + ALU_o1 + 32 + + + add + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + sub + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + and + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + ior + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + xor + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shr + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shru + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shl + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + lt + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + ltu + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + + + + + LSU_i1 + 32 + + + + + LSU_i2 + 32 + + + LSU_o1 + 32 + + + LSU_i3 + 32 + + + ald16 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + ald32 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + ald8 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + aldu8 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + aldu16 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + ast32 + in1t + in2 + in3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + ast16 + in1t + in2 + in3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + ast8 + in1t + in2 + in3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + data + + + + + MUL_DIV_i1 + 32 + + + + + MUL_DIV_i2 + 32 + + + MUL_DIV_o1 + 32 + + + mul + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + mulhi + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + mulhisu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + mulhiu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + div + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + divu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + rem + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + remu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + + + + + CUSTOM_i1 + 32 + + + + + CUSTOM_i2 + 32 + + + CUSTOM_o1 + 32 + + + ones32 + P1 + P3 + + + 0 + 1 + + + 0 + 1 + + + + + + + + normal + 32 + 32 + 2 + 1 + + RF_i1 + + + RF_o1 + + + RF_o2 + + 1 + + + + 8 + 0 + 65536 + + + + 8 + 65540 + 131071 + 1 + + + + + GCU_i1 + 32 + + + + + GCU_i2 + 32 + + + GCU_i3 + 32 + + + GCU_apc_o1 + 32 + + + GCU_ra_o1 + GCU_ra_i1 + 32 + + ra + + jump + pc + + + 0 + 1 + + + + + call + pc + + + 0 + 1 + + + + + callr + pc + + + + beqr + pc + in + in2 + + + + bner + pc + in + in2 + + + + bltr + pc + in + in2 + + + + bltur + pc + in + in2 + + + + calla + pc + in + + + + apc + pc + out + + + + bger + pc + in + in2 + + + + bgeur + pc + in + in2 + + + instructions + 2 + 1 + + + + add + sub + xor + or + and + sll + srl + sra + slt + sltu + div + divu + mul + mulhsu + mulh + mulhu + remu + rem + + + + addi + xori + ori + andi + slli + srli + srai + slti + sltiu + lb + lh + lw + lbu + lhu + jalr + + + + sw + sh + sb + + + + beq + bne + blt + bge + bltu + bgeu + + + + lui + auipc + + + + jal + + + + ones32 + + + + + + + diff --git a/testsuite/systemtest/procgen/ProGe/data/valgen.hdb b/testsuite/systemtest/procgen/ProGe/data/valgen.hdb index a9ad8c853..ca8dd95c1 100644 Binary files a/testsuite/systemtest/procgen/ProGe/data/valgen.hdb and b/testsuite/systemtest/procgen/ProGe/data/valgen.hdb differ diff --git a/testsuite/systemtest/procgen/ProGe/data/valgen.opp b/testsuite/systemtest/procgen/ProGe/data/valgen.opp index a0d9c34f0..771fba41f 100644 --- a/testsuite/systemtest/procgen/ProGe/data/valgen.opp +++ b/testsuite/systemtest/procgen/ProGe/data/valgen.opp @@ -10,4 +10,13 @@ + + ONES32 + + 1 + 1 + + + + diff --git a/testsuite/systemtest/procgen/ProGe/data/valgen32.v b/testsuite/systemtest/procgen/ProGe/data/valgen32.v new file mode 100644 index 000000000..45b0da83b --- /dev/null +++ b/testsuite/systemtest/procgen/ProGe/data/valgen32.v @@ -0,0 +1,2 @@ +op2 = {32{1'b1}}; + diff --git a/testsuite/systemtest/scripts/coproGen_tour/Flist.cva6 b/testsuite/systemtest/scripts/coproGen_tour/Flist.cva6 new file mode 100644 index 000000000..b08d1bf86 --- /dev/null +++ b/testsuite/systemtest/scripts/coproGen_tour/Flist.cva6 @@ -0,0 +1,201 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2021 OpenHW Group +// +// Licensed under the Solderpad Hardware Licence, 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 +// +// https://solderpad.org/licenses/ +// +// 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. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +// +/////////////////////////////////////////////////////////////////////////////// +// +// Manifest for the CVA6 CORE RTL model. +// - This is a CORE-ONLY manifest. +// - Relevent synthesis and simulation scripts/Makefiles must set the shell +// ENV variable CVA6_REPO_DIR. +// +/////////////////////////////////////////////////////////////////////////////// + +//FPGA memories +${CVA6_REPO_DIR}/vendor/pulp-platform/fpga-support/rtl/SyncDpRam.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/fpga-support/rtl/AsyncDpRam.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/fpga-support/rtl/AsyncThreePortRam.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/fpga-support/rtl/SyncThreePortRam.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/fpga-support/rtl/SyncDpRam_ind_r_w.sv + ++incdir+${CVA6_REPO_DIR}/core/include/ ++incdir+${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/include/ ++incdir+${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/ ++incdir+${CVA6_REPO_DIR}/vendor/pulp-platform/axi/include/ ++incdir+${CVA6_REPO_DIR}/common/local/util/ + +// Floating point unit +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_pkg.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_cast_multi.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_classifier.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_divsqrt_multi.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_fma_multi.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_fma.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_noncomp.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_opgroup_block.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_opgroup_fmt_slice.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_opgroup_multifmt_slice.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_rounding.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_top.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpu_div_sqrt_mvp/hdl/defs_div_sqrt_mvp.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpu_div_sqrt_mvp/hdl/control_mvp.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpu_div_sqrt_mvp/hdl/div_sqrt_top_mvp.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpu_div_sqrt_mvp/hdl/iteration_div_sqrt_mvp.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpu_div_sqrt_mvp/hdl/norm_div_sqrt_mvp.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpu_div_sqrt_mvp/hdl/nrbd_nrsc_mvp.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpu_div_sqrt_mvp/hdl/preprocess_mvp.sv + +${CVA6_REPO_DIR}/core/include/config_pkg.sv +${CVA6_REPO_DIR}/core/include/${TARGET_CFG}_config_pkg.sv +${CVA6_REPO_DIR}/core/include/riscv_pkg.sv +// Note: depends on fpnew_pkg, above +${CVA6_REPO_DIR}/core/include/ariane_pkg.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/axi/src/axi_pkg.sv + +// Packages +${CVA6_REPO_DIR}/core/include/wt_cache_pkg.sv +${CVA6_REPO_DIR}/core/include/std_cache_pkg.sv +${CVA6_REPO_DIR}/core/include/instr_tracer_pkg.sv +${CVA6_REPO_DIR}/core/include/build_config_pkg.sv + +//CVXIF +${CVA6_REPO_DIR}/core/cvxif_compressed_if_driver.sv +${CVA6_REPO_DIR}/core/cvxif_issue_register_commit_if_driver.sv +${CVA6_REPO_DIR}/core/cvxif_fu.sv +${CVA6_REPO_DIR}/core/systemverilog/cvxif_sup_pkg.sv +${CVA6_REPO_DIR}/core/systemverilog/custom_coprocessor.sv +${CVA6_REPO_DIR}/core/systemverilog/cvxifcompressed_decoder.sv +${CVA6_REPO_DIR}/core/systemverilog/fu_custom.sv +${CVA6_REPO_DIR}/core/systemverilog/instr_tracker_custom.sv + +// Common Cells +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/cf_math_pkg.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/fifo_v3.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/lfsr.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/lfsr_8bit.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/stream_arbiter.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/stream_arbiter_flushable.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/stream_mux.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/stream_demux.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/lzc.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/rr_arb_tree.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/shift_reg.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/unread.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/popcount.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/exp_backoff.sv + +// Common Cells for example coprocessor +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/counter.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/delta_counter.sv + +// Top-level source files (not necessarily instantiated at the top of the cva6). +${CVA6_REPO_DIR}/core/cva6.sv +${CVA6_REPO_DIR}/core/cva6_rvfi_probes.sv +${CVA6_REPO_DIR}/core/alu.sv +// Note: depends on fpnew_pkg, above +${CVA6_REPO_DIR}/core/fpu_wrap.sv +${CVA6_REPO_DIR}/core/branch_unit.sv +${CVA6_REPO_DIR}/core/compressed_decoder.sv +${CVA6_REPO_DIR}/core/macro_decoder.sv +${CVA6_REPO_DIR}/core/controller.sv +${CVA6_REPO_DIR}/core/zcmt_decoder.sv +${CVA6_REPO_DIR}/core/csr_buffer.sv +${CVA6_REPO_DIR}/core/csr_regfile.sv +${CVA6_REPO_DIR}/core/decoder.sv +${CVA6_REPO_DIR}/core/ex_stage.sv +${CVA6_REPO_DIR}/core/instr_realign.sv +${CVA6_REPO_DIR}/core/id_stage.sv +${CVA6_REPO_DIR}/core/issue_read_operands.sv +${CVA6_REPO_DIR}/core/issue_stage.sv +${CVA6_REPO_DIR}/core/load_unit.sv +${CVA6_REPO_DIR}/core/load_store_unit.sv +${CVA6_REPO_DIR}/core/lsu_bypass.sv +${CVA6_REPO_DIR}/core/mult.sv +${CVA6_REPO_DIR}/core/multiplier.sv +${CVA6_REPO_DIR}/core/serdiv.sv +${CVA6_REPO_DIR}/core/perf_counters.sv +${CVA6_REPO_DIR}/core/ariane_regfile_ff.sv +${CVA6_REPO_DIR}/core/ariane_regfile_fpga.sv +// NOTE: scoreboard.sv modified for DSIM (unchanged for other simulators) +${CVA6_REPO_DIR}/core/scoreboard.sv +${CVA6_REPO_DIR}/core/store_buffer.sv +${CVA6_REPO_DIR}/core/amo_buffer.sv +${CVA6_REPO_DIR}/core/store_unit.sv +${CVA6_REPO_DIR}/core/commit_stage.sv +${CVA6_REPO_DIR}/core/axi_shim.sv +${CVA6_REPO_DIR}/core/cva6_accel_first_pass_decoder_stub.sv +${CVA6_REPO_DIR}/core/acc_dispatcher.sv +${CVA6_REPO_DIR}/core/cva6_fifo_v3.sv + +// What is "frontend"? +${CVA6_REPO_DIR}/core/frontend/btb.sv +${CVA6_REPO_DIR}/core/frontend/bht.sv +//${CVA6_REPO_DIR}/core/frontend/bht2lvl.sv +${CVA6_REPO_DIR}/core/frontend/ras.sv +${CVA6_REPO_DIR}/core/frontend/instr_scan.sv +${CVA6_REPO_DIR}/core/frontend/instr_queue.sv +${CVA6_REPO_DIR}/core/frontend/frontend.sv + +// Cache subsystem +${CVA6_REPO_DIR}/core/cache_subsystem/wt_dcache_ctrl.sv +${CVA6_REPO_DIR}/core/cache_subsystem/wt_dcache_mem.sv +${CVA6_REPO_DIR}/core/cache_subsystem/wt_dcache_missunit.sv +${CVA6_REPO_DIR}/core/cache_subsystem/wt_dcache_wbuffer.sv +${CVA6_REPO_DIR}/core/cache_subsystem/wt_dcache.sv +${CVA6_REPO_DIR}/core/cache_subsystem/cva6_icache.sv +${CVA6_REPO_DIR}/core/cache_subsystem/wt_cache_subsystem.sv +${CVA6_REPO_DIR}/core/cache_subsystem/wt_axi_adapter.sv +${CVA6_REPO_DIR}/core/cache_subsystem/tag_cmp.sv +${CVA6_REPO_DIR}/core/cache_subsystem/axi_adapter.sv +${CVA6_REPO_DIR}/core/cache_subsystem/miss_handler.sv +${CVA6_REPO_DIR}/core/cache_subsystem/cache_ctrl.sv +${CVA6_REPO_DIR}/core/cache_subsystem/cva6_icache_axi_wrapper.sv +${CVA6_REPO_DIR}/core/cache_subsystem/std_cache_subsystem.sv +${CVA6_REPO_DIR}/core/cache_subsystem/std_nbdcache.sv +-F ${HPDCACHE_DIR}/rtl/hpdcache.Flist +${HPDCACHE_DIR}/rtl/src/utils/hpdcache_mem_resp_demux.sv +${HPDCACHE_DIR}/rtl/src/utils/hpdcache_mem_to_axi_read.sv +${HPDCACHE_DIR}/rtl/src/utils/hpdcache_mem_to_axi_write.sv +${CVA6_REPO_DIR}/core/cache_subsystem/cva6_hpdcache_subsystem.sv +${CVA6_REPO_DIR}/core/cache_subsystem/cva6_hpdcache_subsystem_axi_arbiter.sv +${CVA6_REPO_DIR}/core/cache_subsystem/cva6_hpdcache_if_adapter.sv +${CVA6_REPO_DIR}/core/cache_subsystem/cva6_hpdcache_wrapper.sv +${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_1rw.sv +${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_wbyteenable_1rw.sv +${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_wmask_1rw.sv + +// Physical Memory Protection +// NOTE: pmp.sv modified for DSIM (unchanged for other simulators) +${CVA6_REPO_DIR}/core/pmp/src/pmp.sv +${CVA6_REPO_DIR}/core/pmp/src/pmp_entry.sv +${CVA6_REPO_DIR}/core/pmp/src/pmp_data_if.sv + +// Tracer (behavioral code, not RTL) +${CVA6_REPO_DIR}/common/local/util/instr_tracer.sv +${CVA6_REPO_DIR}/common/local/util/tc_sram_wrapper.sv +${CVA6_REPO_DIR}/common/local/util/tc_sram_wrapper_cache_techno.sv +${CVA6_REPO_DIR}/vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv +${CVA6_REPO_DIR}/common/local/util/sram.sv +${CVA6_REPO_DIR}/common/local/util/sram_cache.sv + +// MMU +${CVA6_REPO_DIR}/core/cva6_mmu/cva6_mmu.sv +${CVA6_REPO_DIR}/core/cva6_mmu/cva6_ptw.sv +${CVA6_REPO_DIR}/core/cva6_mmu/cva6_tlb.sv +${CVA6_REPO_DIR}/core/cva6_mmu/cva6_shared_tlb.sv + +// end of manifest diff --git a/testsuite/systemtest/scripts/coproGen_tour/LazyRoCC.scala b/testsuite/systemtest/scripts/coproGen_tour/LazyRoCC.scala new file mode 100644 index 000000000..a1d9f37e5 --- /dev/null +++ b/testsuite/systemtest/scripts/coproGen_tour/LazyRoCC.scala @@ -0,0 +1,405 @@ +// See LICENSE.Berkeley for license details. +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.tile + +import chisel3._ +import chisel3.util._ +import chisel3.experimental.IntParam + +import org.chipsalliance.cde.config._ +import org.chipsalliance.diplomacy.lazymodule._ + +import freechips.rocketchip.rocket.{ + MStatus, HellaCacheIO, TLBPTWIO, CanHavePTW, CanHavePTWModule, + SimpleHellaCacheIF, M_XRD, PTE, PRV, M_SZ +} +import freechips.rocketchip.tilelink.{ + TLNode, TLIdentityNode, TLClientNode, TLMasterParameters, TLMasterPortParameters +} +import freechips.rocketchip.util.InOrderArbiter + +case object BuildRoCC extends Field[Seq[Parameters => LazyRoCC]](Nil) + +class RoCCInstruction extends Bundle { + val funct = Bits(7.W) + val rs2 = Bits(5.W) + val rs1 = Bits(5.W) + val xd = Bool() + val xs1 = Bool() + val xs2 = Bool() + val rd = Bits(5.W) + val opcode = Bits(7.W) +} + +class RoCCCommand(implicit p: Parameters) extends CoreBundle()(p) { + val inst = new RoCCInstruction + val rs1 = Bits(xLen.W) + val rs2 = Bits(xLen.W) + val status = new MStatus +} + +class RoCCResponse(implicit p: Parameters) extends CoreBundle()(p) { + val rd = Bits(5.W) + val data = Bits(xLen.W) +} + +class RoCCCoreIO(val nRoCCCSRs: Int = 0)(implicit p: Parameters) extends CoreBundle()(p) { + val cmd = Flipped(Decoupled(new RoCCCommand)) + val resp = Decoupled(new RoCCResponse) + val mem = new HellaCacheIO + val busy = Output(Bool()) + val interrupt = Output(Bool()) + val exception = Input(Bool()) + val csrs = Flipped(Vec(nRoCCCSRs, new CustomCSRIO)) +} + +class RoCCIO(val nPTWPorts: Int, nRoCCCSRs: Int)(implicit p: Parameters) extends RoCCCoreIO(nRoCCCSRs)(p) { + val ptw = Vec(nPTWPorts, new TLBPTWIO) + val fpu_req = Decoupled(new FPInput) + val fpu_resp = Flipped(Decoupled(new FPResult)) +} + +/** Base classes for Diplomatic TL2 RoCC units **/ +abstract class LazyRoCC( + val opcodes: OpcodeSet, + val nPTWPorts: Int = 0, + val usesFPU: Boolean = false, + val roccCSRs: Seq[CustomCSR] = Nil +)(implicit p: Parameters) extends LazyModule { + val module: LazyRoCCModuleImp + require(roccCSRs.map(_.id).toSet.size == roccCSRs.size) + val atlNode: TLNode = TLIdentityNode() + val tlNode: TLNode = TLIdentityNode() + val stlNode: TLNode = TLIdentityNode() +} + +class LazyRoCCModuleImp(outer: LazyRoCC) extends LazyModuleImp(outer) { + val io = IO(new RoCCIO(outer.nPTWPorts, outer.roccCSRs.size)) + io := DontCare +} + +/** Mixins for including RoCC **/ + +trait HasLazyRoCC extends CanHavePTW { this: BaseTile => + val roccs = p(BuildRoCC).map(_(p)) + val roccCSRs = roccs.map(_.roccCSRs) // the set of custom CSRs requested by all roccs + require(roccCSRs.flatten.map(_.id).toSet.size == roccCSRs.flatten.size, + "LazyRoCC instantiations require overlapping CSRs") + roccs.map(_.atlNode).foreach { atl => tlMasterXbar.node :=* atl } + roccs.map(_.tlNode).foreach { tl => tlOtherMastersNode :=* tl } + roccs.map(_.stlNode).foreach { stl => stl :*= tlSlaveXbar.node } + + nPTWPorts += roccs.map(_.nPTWPorts).sum + nDCachePorts += roccs.size +} + +trait HasLazyRoCCModule extends CanHavePTWModule + with HasCoreParameters { this: RocketTileModuleImp => + + val (respArb, cmdRouter) = if(outer.roccs.nonEmpty) { + val respArb = Module(new RRArbiter(new RoCCResponse()(outer.p), outer.roccs.size)) + val cmdRouter = Module(new RoccCommandRouter(outer.roccs.map(_.opcodes))(outer.p)) + outer.roccs.zipWithIndex.foreach { case (rocc, i) => + rocc.module.io.ptw ++=: ptwPorts + rocc.module.io.cmd <> cmdRouter.io.out(i) + val dcIF = Module(new SimpleHellaCacheIF()(outer.p)) + dcIF.io.requestor <> rocc.module.io.mem + dcachePorts += dcIF.io.cache + respArb.io.in(i) <> Queue(rocc.module.io.resp) + } + (Some(respArb), Some(cmdRouter)) + } else { + (None, None) + } + val roccCSRIOs = outer.roccs.map(_.module.io.csrs) +} + +class AccumulatorExample(opcodes: OpcodeSet, val n: Int = 4)(implicit p: Parameters) extends LazyRoCC(opcodes) { + override lazy val module = new AccumulatorExampleModuleImp(this) +} + +class AccumulatorExampleModuleImp(outer: AccumulatorExample)(implicit p: Parameters) extends LazyRoCCModuleImp(outer) + with HasCoreParameters { + val regfile = Mem(outer.n, UInt(xLen.W)) + val busy = RegInit(VecInit(Seq.fill(outer.n){false.B})) + + val cmd = Queue(io.cmd) + val funct = cmd.bits.inst.funct + val addr = cmd.bits.rs2(log2Up(outer.n)-1,0) + val doWrite = funct === 0.U + val doRead = funct === 1.U + val doLoad = funct === 2.U + val doAccum = funct === 3.U + val memRespTag = io.mem.resp.bits.tag(log2Up(outer.n)-1,0) + + // datapath + val addend = cmd.bits.rs1 + val accum = regfile(addr) + val wdata = Mux(doWrite, addend, accum + addend) + + when (cmd.fire && (doWrite || doAccum)) { + regfile(addr) := wdata + } + + when (io.mem.resp.valid) { + regfile(memRespTag) := io.mem.resp.bits.data + busy(memRespTag) := false.B + } + + // control + when (io.mem.req.fire) { + busy(addr) := true.B + } + + val doResp = cmd.bits.inst.xd + val stallReg = busy(addr) + val stallLoad = doLoad && !io.mem.req.ready + val stallResp = doResp && !io.resp.ready + + cmd.ready := !stallReg && !stallLoad && !stallResp + // command resolved if no stalls AND not issuing a load that will need a request + + // PROC RESPONSE INTERFACE + io.resp.valid := cmd.valid && doResp && !stallReg && !stallLoad + // valid response if valid command, need a response, and no stalls + io.resp.bits.rd := cmd.bits.inst.rd + // Must respond with the appropriate tag or undefined behavior + io.resp.bits.data := accum + // Semantics is to always send out prior accumulator register value + + io.busy := cmd.valid || busy.reduce(_||_) + // Be busy when have pending memory requests or committed possibility of pending requests + io.interrupt := false.B + // Set this true to trigger an interrupt on the processor (please refer to supervisor documentation) + + // MEMORY REQUEST INTERFACE + io.mem.req.valid := cmd.valid && doLoad && !stallReg && !stallResp + io.mem.req.bits.addr := addend + io.mem.req.bits.tag := addr + io.mem.req.bits.cmd := M_XRD // perform a load (M_XWR for stores) + io.mem.req.bits.size := log2Ceil(8).U + io.mem.req.bits.signed := false.B + io.mem.req.bits.data := 0.U // we're not performing any stores... + io.mem.req.bits.phys := false.B + io.mem.req.bits.dprv := cmd.bits.status.dprv + io.mem.req.bits.dv := cmd.bits.status.dv + io.mem.req.bits.no_resp := false.B +} + +class TranslatorExample(opcodes: OpcodeSet)(implicit p: Parameters) extends LazyRoCC(opcodes, nPTWPorts = 1) { + override lazy val module = new TranslatorExampleModuleImp(this) +} + +class TranslatorExampleModuleImp(outer: TranslatorExample)(implicit p: Parameters) extends LazyRoCCModuleImp(outer) + with HasCoreParameters { + val req_addr = Reg(UInt(coreMaxAddrBits.W)) + val req_rd = Reg(chiselTypeOf(io.resp.bits.rd)) + val req_offset = req_addr(pgIdxBits - 1, 0) + val req_vpn = req_addr(coreMaxAddrBits - 1, pgIdxBits) + val pte = Reg(new PTE) + + val s_idle :: s_ptw_req :: s_ptw_resp :: s_resp :: Nil = Enum(4) + val state = RegInit(s_idle) + + io.cmd.ready := (state === s_idle) + + when (io.cmd.fire) { + req_rd := io.cmd.bits.inst.rd + req_addr := io.cmd.bits.rs1 + state := s_ptw_req + } + + private val ptw = io.ptw(0) + + when (ptw.req.fire) { state := s_ptw_resp } + + when (state === s_ptw_resp && ptw.resp.valid) { + pte := ptw.resp.bits.pte + state := s_resp + } + + when (io.resp.fire) { state := s_idle } + + ptw.req.valid := (state === s_ptw_req) + ptw.req.bits.valid := true.B + ptw.req.bits.bits.addr := req_vpn + + io.resp.valid := (state === s_resp) + io.resp.bits.rd := req_rd + io.resp.bits.data := Mux(pte.leaf(), Cat(pte.ppn, req_offset), -1.S(xLen.W).asUInt) + + io.busy := (state =/= s_idle) + io.interrupt := false.B + io.mem.req.valid := false.B +} + +class CharacterCountExample(opcodes: OpcodeSet)(implicit p: Parameters) extends LazyRoCC(opcodes) { + override lazy val module = new CharacterCountExampleModuleImp(this) + override val atlNode = TLClientNode(Seq(TLMasterPortParameters.v1(Seq(TLMasterParameters.v1("CharacterCountRoCC"))))) +} + +class CharacterCountExampleModuleImp(outer: CharacterCountExample)(implicit p: Parameters) extends LazyRoCCModuleImp(outer) + with HasCoreParameters + with HasL1CacheParameters { + val cacheParams = tileParams.dcache.get + + private val blockOffset = blockOffBits + private val beatOffset = log2Up(cacheDataBits/8) + + val needle = Reg(UInt(8.W)) + val addr = Reg(UInt(coreMaxAddrBits.W)) + val count = Reg(UInt(xLen.W)) + val resp_rd = Reg(chiselTypeOf(io.resp.bits.rd)) + + val addr_block = addr(coreMaxAddrBits - 1, blockOffset) + val offset = addr(blockOffset - 1, 0) + val next_addr = (addr_block + 1.U) << blockOffset.U + + val s_idle :: s_acq :: s_gnt :: s_check :: s_resp :: Nil = Enum(5) + val state = RegInit(s_idle) + + val (tl_out, edgesOut) = outer.atlNode.out(0) + val gnt = tl_out.d.bits + val recv_data = Reg(UInt(cacheDataBits.W)) + val recv_beat = RegInit(0.U(log2Up(cacheDataBeats+1).W)) + + val data_bytes = VecInit(Seq.tabulate(cacheDataBits/8) { i => recv_data(8 * (i + 1) - 1, 8 * i) }) + val zero_match = data_bytes.map(_ === 0.U) + val needle_match = data_bytes.map(_ === needle) + val first_zero = PriorityEncoder(zero_match) + + val chars_found = PopCount(needle_match.zipWithIndex.map { + case (matches, i) => + val idx = Cat(recv_beat - 1.U, i.U(beatOffset.W)) + matches && idx >= offset && i.U <= first_zero + }) + val zero_found = zero_match.reduce(_ || _) + val finished = Reg(Bool()) + + io.cmd.ready := (state === s_idle) + io.resp.valid := (state === s_resp) + io.resp.bits.rd := resp_rd + io.resp.bits.data := count + tl_out.a.valid := (state === s_acq) + tl_out.a.bits := edgesOut.Get( + fromSource = 0.U, + toAddress = addr_block << blockOffset, + lgSize = lgCacheBlockBytes.U)._2 + tl_out.d.ready := (state === s_gnt) + + when (io.cmd.fire) { + addr := io.cmd.bits.rs1 + needle := io.cmd.bits.rs2 + resp_rd := io.cmd.bits.inst.rd + count := 0.U + finished := false.B + state := s_acq + } + + when (tl_out.a.fire) { state := s_gnt } + + when (tl_out.d.fire) { + recv_beat := recv_beat + 1.U + recv_data := gnt.data + state := s_check + } + + when (state === s_check) { + when (!finished) { + count := count + chars_found + } + when (zero_found) { finished := true.B } + when (recv_beat === cacheDataBeats.U) { + addr := next_addr + state := Mux(zero_found || finished, s_resp, s_acq) + recv_beat := 0.U + } .otherwise { + state := s_gnt + } + } + + when (io.resp.fire) { state := s_idle } + + io.busy := (state =/= s_idle) + io.interrupt := false.B + io.mem.req.valid := false.B + // Tie off unused channels + tl_out.b.ready := true.B + tl_out.c.valid := false.B + tl_out.e.valid := false.B +} + +class BlackBoxExample(opcodes: OpcodeSet, blackBoxFile: String)(implicit p: Parameters) + extends LazyRoCC(opcodes) { + override lazy val module = new BlackBoxExampleModuleImp(this, blackBoxFile) +} + +class BlackBoxExampleModuleImp(outer: BlackBoxExample, blackBoxFile: String)(implicit p: Parameters) + extends LazyRoCCModuleImp(outer) + with RequireSyncReset + with HasCoreParameters { + + val blackbox = { + val roccIo = io + Module( + new BlackBox( Map( "xLen" -> IntParam(xLen) + ) ) with HasBlackBoxResource { + val io = IO( new Bundle { + val clock = Input(Clock()) + val reset = Input(Reset()) + val cmd = chiselTypeOf(roccIo.cmd) + val resp = chiselTypeOf(roccIo.resp) + val busy = chiselTypeOf(roccIo.busy) + }) + override def desiredName: String = blackBoxFile + "_coprocessor" + addResource(s"/vsrc/coprocessor_$blackBoxFile.sv") + addResource(s"/vsrc/fu_$blackBoxFile.sv") + } + ) + } + + blackbox.io.clock := clock + blackbox.io.reset := reset + blackbox.io.cmd <> io.cmd + io.resp <> blackbox.io.resp + io.busy := blackbox.io.busy + +} + +class OpcodeSet(val opcodes: Seq[UInt]) { + def |(set: OpcodeSet) = + new OpcodeSet(this.opcodes ++ set.opcodes) + + def matches(oc: UInt) = opcodes.map(_ === oc).reduce(_ || _) +} + +object OpcodeSet { + def custom0 = new OpcodeSet(Seq("b0001011".U)) + def custom1 = new OpcodeSet(Seq("b0101011".U)) + def custom2 = new OpcodeSet(Seq("b1011011".U)) + def custom3 = new OpcodeSet(Seq("b1111011".U)) + def all = custom0 | custom1 | custom2 | custom3 +} + +class RoccCommandRouter(opcodes: Seq[OpcodeSet])(implicit p: Parameters) + extends CoreModule()(p) { + val io = IO(new Bundle { + val in = Flipped(Decoupled(new RoCCCommand)) + val out = Vec(opcodes.size, Decoupled(new RoCCCommand)) + val busy = Output(Bool()) + }) + + val cmd = Queue(io.in) + val cmdReadys = io.out.zip(opcodes).map { case (out, opcode) => + val me = opcode.matches(cmd.bits.inst.opcode) + out.valid := cmd.valid && me + out.bits := cmd.bits + out.ready && me + } + cmd.ready := cmdReadys.reduce(_ || _) + io.busy := cmd.valid + + assert(PopCount(cmdReadys) <= 1.U, + "Custom opcode matched for more than one accelerator") +} diff --git a/testsuite/systemtest/scripts/coproGen_tour/RoCCFragments.scala b/testsuite/systemtest/scripts/coproGen_tour/RoCCFragments.scala new file mode 100644 index 000000000..433e164a5 --- /dev/null +++ b/testsuite/systemtest/scripts/coproGen_tour/RoCCFragments.scala @@ -0,0 +1,66 @@ +package chipyard.config + +import chisel3._ + +import org.chipsalliance.cde.config.{Field, Parameters, Config} +import freechips.rocketchip.tile._ +import freechips.rocketchip.diplomacy._ + +import gemmini._ + +import chipyard.{TestSuitesKey, TestSuiteHelper} + +/** + * Map from a tileId to a particular RoCC accelerator + */ +case object MultiRoCCKey extends Field[Map[Int, Seq[Parameters => LazyRoCC]]](Map.empty[Int, Seq[Parameters => LazyRoCC]]) + +/** + * Config fragment to enable different RoCCs based on the tileId + */ +class WithMultiRoCC extends Config((site, here, up) => { + case BuildRoCC => site(MultiRoCCKey).getOrElse(site(TileKey).tileId, Nil) +}) + +/** + * Assigns what was previously in the BuildRoCC key to specific harts with MultiRoCCKey + * Must be paired with WithMultiRoCC + */ +class WithMultiRoCCFromBuildRoCC(harts: Int*) extends Config((site, here, up) => { + case BuildRoCC => Nil + case MultiRoCCKey => up(MultiRoCCKey, site) ++ harts.distinct.map { i => + (i -> up(BuildRoCC, site)) + } +}) + +class WithMultiRoCCGemmini[T <: Data : Arithmetic, U <: Data, V <: Data]( + harts: Int*)(gemminiConfig: GemminiArrayConfig[T,U,V] = GemminiConfigs.defaultConfig) extends Config((site, here, up) => { + case MultiRoCCKey => up(MultiRoCCKey, site) ++ harts.distinct.map { i => + (i -> Seq((p: Parameters) => { + implicit val q = p + val gemmini = LazyModule(new Gemmini(gemminiConfig)) + gemmini + })) + } +}) + +class WithAccumulatorRoCC(op: OpcodeSet = OpcodeSet.custom1) extends Config((site, here, up) => { + case BuildRoCC => up(BuildRoCC) ++ Seq((p: Parameters) => { + val accumulator = LazyModule(new AccumulatorExample(op, n = 4)(p)) + accumulator + }) +}) + +class WithCharacterCountRoCC(op: OpcodeSet = OpcodeSet.custom2) extends Config((site, here, up) => { + case BuildRoCC => up(BuildRoCC) ++ Seq((p: Parameters) => { + val counter = LazyModule(new CharacterCountExample(op)(p)) + counter + }) +}) + +class WithBlackboxRoCC(op: OpcodeSet = OpcodeSet.custom0) extends Config((site, here, up) => { + case BuildRoCC => up(BuildRoCC) ++ Seq((p: Parameters) => { + val coproadder = LazyModule(new BlackBoxExample(OpcodeSet.custom0, "custom")(p)) + coproadder + }) +}) diff --git a/testsuite/systemtest/scripts/coproGen_tour/RocketConfigs.scala b/testsuite/systemtest/scripts/coproGen_tour/RocketConfigs.scala new file mode 100644 index 000000000..8fabeb954 --- /dev/null +++ b/testsuite/systemtest/scripts/coproGen_tour/RocketConfigs.scala @@ -0,0 +1,123 @@ +package chipyard + +import org.chipsalliance.cde.config.{Config} +import freechips.rocketchip.prci.{AsynchronousCrossing} +import freechips.rocketchip.subsystem.{InCluster} + +// -------------- +// Rocket Configs +// -------------- + +class RocketConfig extends Config( + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ // single rocket-core + new chipyard.config.AbstractConfig) + +class DualRocketConfig extends Config( + new freechips.rocketchip.rocket.WithNHugeCores(2) ++ + new chipyard.config.AbstractConfig) + +class SmallrocketConfig extends Config( // small rocket config + new freechips.rocketchip.rocket.WithNSmallCores(1) ++ + new chipyard.config.WithBlackboxRoCC ++ + new chipyard.config.AbstractConfig) + +class TinyRocketConfig extends Config( + new chipyard.harness.WithDontTouchChipTopPorts(false) ++ // TODO FIX: Don't dontTouch the ports + new testchipip.soc.WithNoScratchpads ++ // All memory is the Rocket TCMs + new freechips.rocketchip.subsystem.WithIncoherentBusTopology ++ // use incoherent bus topology + new freechips.rocketchip.subsystem.WithNBanks(0) ++ // remove L2$ + new freechips.rocketchip.subsystem.WithNoMemPort ++ // remove backing memory + new freechips.rocketchip.rocket.With1TinyCore ++ // single tiny rocket-core + new chipyard.config.WithBlackboxRoCC ++ + new chipyard.config.AbstractConfig) + +class QuadRocketConfig extends Config( + new freechips.rocketchip.rocket.WithNHugeCores(4) ++ // quad-core (4 RocketTiles) + new chipyard.config.AbstractConfig) + +class Cloned64RocketConfig extends Config( + new freechips.rocketchip.rocket.WithCloneRocketTiles(63, 0) ++ // copy tile0 63 more times + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ // tile0 is a BigRocket + new chipyard.config.AbstractConfig) + +class RV32RocketConfig extends Config( + new freechips.rocketchip.rocket.WithRV32 ++ // set RocketTiles to be 32-bit + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ + new chipyard.config.AbstractConfig) + +// DOC include start: l1scratchpadrocket +class ScratchpadOnlyRocketConfig extends Config( + new chipyard.config.WithL2TLBs(0) ++ + new testchipip.soc.WithNoScratchpads ++ // remove subsystem scratchpads, confusingly named, does not remove the L1D$ scratchpads + new freechips.rocketchip.subsystem.WithNBanks(0) ++ + new freechips.rocketchip.subsystem.WithNoMemPort ++ // remove offchip mem port + new freechips.rocketchip.rocket.WithScratchpadsOnly ++ // use rocket l1 DCache scratchpad as base phys mem + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ + new chipyard.config.AbstractConfig) +// DOC include end: l1scratchpadrocket + +class MMIOScratchpadOnlyRocketConfig extends Config( + new freechips.rocketchip.subsystem.WithDefaultMMIOPort ++ // add default external master port + new freechips.rocketchip.subsystem.WithDefaultSlavePort ++ // add default external slave port + new chipyard.config.WithL2TLBs(0) ++ + new testchipip.soc.WithNoScratchpads ++ // remove subsystem scratchpads, confusingly named, does not remove the L1D$ scratchpads + new freechips.rocketchip.subsystem.WithNBanks(0) ++ + new freechips.rocketchip.subsystem.WithNoMemPort ++ // remove offchip mem port + new freechips.rocketchip.rocket.WithScratchpadsOnly ++ // use rocket l1 DCache scratchpad as base phys mem + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ + new chipyard.config.AbstractConfig) + +class L1ScratchpadRocketConfig extends Config( + new chipyard.config.WithRocketICacheScratchpad ++ // use rocket ICache scratchpad + new chipyard.config.WithRocketDCacheScratchpad ++ // use rocket DCache scratchpad + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ + new chipyard.config.AbstractConfig) + +class MulticlockRocketConfig extends Config( + new freechips.rocketchip.rocket.WithAsynchronousCDCs(8, 3) ++ // Add async crossings between RocketTile and uncore + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ + // Frequency specifications + new chipyard.config.WithTileFrequency(1000.0) ++ // Matches the maximum frequency of U540 + new chipyard.clocking.WithClockGroupsCombinedByName(("uncore" , Seq("sbus", "cbus", "implicit", "clock_tap"), Nil), + ("periphery", Seq("pbus", "fbus"), Nil)) ++ + new chipyard.config.WithSystemBusFrequency(500.0) ++ // Matches the maximum frequency of U540 + new chipyard.config.WithMemoryBusFrequency(500.0) ++ // Matches the maximum frequency of U540 + new chipyard.config.WithPeripheryBusFrequency(500.0) ++ // Matches the maximum frequency of U540 + // Crossing specifications + new chipyard.config.WithFbusToSbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossing between FBUS and SBUS + new chipyard.config.WithCbusToPbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossing between PBUS and CBUS + new chipyard.config.WithSbusToMbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossings between backside of L2 and MBUS + new chipyard.config.AbstractConfig) + +class CustomIOChipTopRocketConfig extends Config( + new chipyard.example.WithBrokenOutUARTIO ++ + new chipyard.example.WithCustomChipTop ++ + new chipyard.example.WithCustomIOCells ++ + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ // single rocket-core + new chipyard.config.AbstractConfig) + +class PrefetchingRocketConfig extends Config( + new barf.WithHellaCachePrefetcher(Seq(0), barf.SingleStridedPrefetcherParams()) ++ // strided prefetcher, sits in front of the L1D$, monitors core requests to prefetching into the L1D$ + new barf.WithTLICachePrefetcher(barf.MultiNextLinePrefetcherParams()) ++ // next-line prefetcher, sits between L1I$ and L2, monitors L1I$ misses to prefetch into L2 + new barf.WithTLDCachePrefetcher(barf.SingleAMPMPrefetcherParams()) ++ // AMPM prefetcher, sits between L1D$ and L2, monitors L1D$ misses to prefetch into L2 + new chipyard.config.WithTilePrefetchers ++ // add TL prefetchers between tiles and the sbus + new freechips.rocketchip.rocket.WithL1DCacheNonblocking(2) ++ // non-blocking L1D$, L1 prefetching only works with non-blocking L1D$ + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ // single rocket-core + new chipyard.config.AbstractConfig) + +class ClusteredRocketConfig extends Config( + new freechips.rocketchip.rocket.WithNHugeCores(4, location=InCluster(1)) ++ + new freechips.rocketchip.rocket.WithNHugeCores(4, location=InCluster(0)) ++ + new freechips.rocketchip.subsystem.WithCluster(1) ++ + new freechips.rocketchip.subsystem.WithCluster(0) ++ + new chipyard.config.AbstractConfig) + +class FastRTLSimRocketConfig extends Config( + new freechips.rocketchip.subsystem.WithoutTLMonitors ++ + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ + new chipyard.config.AbstractConfig) + +class SV48RocketConfig extends Config( + new freechips.rocketchip.rocket.WithSV48 ++ + new freechips.rocketchip.rocket.WithNHugeCores(1) ++ + new chipyard.config.AbstractConfig) diff --git a/testsuite/systemtest/scripts/coproGen_tour/ariane.sv b/testsuite/systemtest/scripts/coproGen_tour/ariane.sv new file mode 100644 index 000000000..67b3645b6 --- /dev/null +++ b/testsuite/systemtest/scripts/coproGen_tour/ariane.sv @@ -0,0 +1,143 @@ +// Copyright 2017-2019 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this 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. +// +// Author: Florian Zaruba, ETH Zurich +// Date: 19.03.2017 +// Description: Ariane Top-level module + +`include "cvxif_types.svh" + +module ariane import ariane_pkg::*; #( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter type rvfi_probes_instr_t = logic, + parameter type rvfi_probes_csr_t = logic, + parameter type rvfi_probes_t = struct packed { + logic csr; + logic instr; + }, + // CVXIF Types + localparam type readregflags_t = `READREGFLAGS_T(CVA6Cfg), + localparam type writeregflags_t = `WRITEREGFLAGS_T(CVA6Cfg), + localparam type id_t = `ID_T(CVA6Cfg), + localparam type hartid_t = `HARTID_T(CVA6Cfg), + localparam type x_compressed_req_t = `X_COMPRESSED_REQ_T(CVA6Cfg, hartid_t), + localparam type x_compressed_resp_t = `X_COMPRESSED_RESP_T(CVA6Cfg), + localparam type x_issue_req_t = `X_ISSUE_REQ_T(CVA6Cfg, hartit_t, id_t), + localparam type x_issue_resp_t = `X_ISSUE_RESP_T(CVA6Cfg, writeregflags_t, readregflags_t), + localparam type x_register_t = `X_REGISTER_T(CVA6Cfg, hartid_t, id_t, readregflags_t), + localparam type x_commit_t = `X_COMMIT_T(CVA6Cfg, hartid_t, id_t), + localparam type x_result_t = `X_RESULT_T(CVA6Cfg, hartid_t, id_t, writeregflags_t), + localparam type cvxif_req_t = `CVXIF_REQ_T(CVA6Cfg, x_compressed_req_t, x_issue_req_t, x_register_req_t, x_commit_t), + localparam type cvxif_resp_t = `CVXIF_RESP_T(CVA6Cfg, x_compressed_resp_t, x_issue_resp_t, x_result_t), + // AXI Types + parameter int unsigned AxiAddrWidth = ariane_axi::AddrWidth, + parameter int unsigned AxiDataWidth = ariane_axi::DataWidth, + parameter int unsigned AxiIdWidth = ariane_axi::IdWidth, + parameter type axi_ar_chan_t = ariane_axi::ar_chan_t, + parameter type axi_aw_chan_t = ariane_axi::aw_chan_t, + parameter type axi_w_chan_t = ariane_axi::w_chan_t, + parameter type noc_req_t = ariane_axi::req_t, + parameter type noc_resp_t = ariane_axi::resp_t +) ( + input logic clk_i, + input logic rst_ni, + // Core ID, Cluster ID and boot address are considered more or less static + input logic [CVA6Cfg.VLEN-1:0] boot_addr_i, // reset boot address + input logic [CVA6Cfg.XLEN-1:0] hart_id_i, // hart id in a multicore environment (reflected in a CSR) + + // Interrupt inputs + input logic [1:0] irq_i, // level sensitive IR lines, mip & sip (async) + input logic ipi_i, // inter-processor interrupts (async) + // Timer facilities + input logic time_irq_i, // timer interrupt in (async) + input logic debug_req_i, // debug request (async) + // RISC-V formal interface port (`rvfi`): + // Can be left open when formal tracing is not needed. + output rvfi_probes_t rvfi_probes_o, + // memory side + output noc_req_t noc_req_o, + input noc_resp_t noc_resp_i +); + + cvxif_req_t cvxif_req; + cvxif_resp_t cvxif_resp; + + cva6 #( + .CVA6Cfg ( CVA6Cfg ), + .rvfi_probes_instr_t ( rvfi_probes_instr_t ), + .rvfi_probes_csr_t ( rvfi_probes_csr_t ), + .rvfi_probes_t ( rvfi_probes_t ), + .axi_ar_chan_t (axi_ar_chan_t), + .axi_aw_chan_t (axi_aw_chan_t), + .axi_w_chan_t (axi_w_chan_t), + .noc_req_t (noc_req_t), + .noc_resp_t (noc_resp_t), + .readregflags_t (readregflags_t), + .writeregflags_t (writeregflags_t), + .id_t (id_t), + .hartid_t (hartid_t), + .x_compressed_req_t (x_compressed_req_t), + .x_compressed_resp_t (x_compressed_resp_t), + .x_issue_req_t (x_issue_req_t), + .x_issue_resp_t (x_issue_resp_t), + .x_register_t (x_register_t), + .x_commit_t (x_commit_t), + .x_result_t (x_result_t), + .cvxif_req_t (cvxif_req_t), + .cvxif_resp_t (cvxif_resp_t) + ) i_cva6 ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .boot_addr_i ( boot_addr_i ), + .hart_id_i ( hart_id_i ), + .irq_i ( irq_i ), + .ipi_i ( ipi_i ), + .time_irq_i ( time_irq_i ), + .debug_req_i ( debug_req_i ), + .rvfi_probes_o ( rvfi_probes_o ), + .cvxif_req_o ( cvxif_req ), + .cvxif_resp_i ( cvxif_resp ), + .noc_req_o ( noc_req_o ), + .noc_resp_i ( noc_resp_i ) + ); + + if (CVA6Cfg.CvxifEn) begin : gen_example_coprocessor + custom_coprocessor #( + .NrRgprPorts (CVA6Cfg.NrRgprPorts), + .readregflags_t (readregflags_t), + .writeregflags_t (writeregflags_t), + .id_t (id_t), + .hartid_t (hartid_t), + .x_compressed_req_t (x_compressed_req_t), + .x_compressed_resp_t (x_compressed_resp_t), + .x_issue_req_t (x_issue_req_t), + .x_issue_resp_t (x_issue_resp_t), + .x_register_t (x_register_t), + .x_commit_t (x_commit_t), + .x_result_t (x_result_t), + .cvxif_req_t (cvxif_req_t), + .cvxif_resp_t (cvxif_resp_t) + ) i_cvxif_coprocessor ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .cvxif_req_i ( cvxif_req ), + .cvxif_resp_o ( cvxif_resp ) + ); + end else begin + always_comb begin + cvxif_resp = '0; + cvxif_resp.compressed_ready = 1'b1; + cvxif_resp.issue_ready = 1'b1; + cvxif_resp.register_ready = 1'b1; + end + end + + +endmodule // ariane diff --git a/testsuite/systemtest/scripts/coproGen_tour/coproGen_tour.f b/testsuite/systemtest/scripts/coproGen_tour/coproGen_tour.f new file mode 100755 index 000000000..a3a6158e3 --- /dev/null +++ b/testsuite/systemtest/scripts/coproGen_tour/coproGen_tour.f @@ -0,0 +1,72 @@ +#!/bin/bash +### title: Tour script for the Coprocessor generation + +TOUR_PATH="${OPENASIP_PATH}/testsuite/systemtest/scripts/coproGen_tour" +adf_path="coprogen.adf" + +#CVA6 +flist_path="${TOUR_PATH}/Flist.cva6" +ariane_path="${TOUR_PATH}/ariane.sv" +svfiles_path="out_cvx/systemverilog" +c_programs="main.c crc.c" +linked_file="final.o" +linker="${cva6_path}/config/gen_from_riscv_config/linker/link.ld" +DV_SIMULATORS="veri-testharness" + +#RoCC +roccgen_path="${ROCC_PATH}/generators" +chipyardconfig_path="${roccgen_path}/chipyard/src/main/scala/config" +rocketmain_path="${roccgen_path}/rocket-chip/src/main" + +if [[ "${INTERFACE}" -eq 1 ]]; then + cp ${flist_path} ${cva6_path}/core + cp ${ariane_path} ${cva6_path}/corev_apu/src/ + cp -r ${svfiles_path} ${cva6_path}/core/ + + test_o="${cva6_path}/verif/tests/custom/simple_test/test.o" + syscall_o="${cva6_path}/verif/tests/custom/common/syscall.o" + crt_o="${cva6_path}/verif/tests/custom/common/crt.o" + + oacc_flags="--unroll-threshold=0 -I ../../ -d -v -c -O3 --mattr "+c,+m,+zba,+zbb,+zbs,+zbc,+ziscr,+zifencei" --march=riscv32" + gcc_flags="-c -O3 -march=rv32imc_zba_zbb_zbs_zbc_zicsr_zifencei -mabi=ilp32 -ffunction-sections -fdata-sections -I ${cva6_path}/verif/tests/custom/env -I ${cva6_path}/verif/tests/custom/common" + + riscv-none-elf-gcc ${gcc_flags} -o crt.o ${cva6_path}/verif/tests/custom/common/crt.S + riscv-none-elf-gcc ${gcc_flags} -o syscall.o ${cva6_path}/verif/tests/custom/common/syscalls.c + + oacc-riscv ${oacc_flags} -a ${adf_path} -o compiled.o ${c_programs} + riscv-none-elf-ld -L ${cva6_path}/util/toolchain-builder/build/gcc/gcc/rv32im/ilp32 -o ${linked_file} -T ${linker} compiled.o crt.o syscall.o -lgcc + cp ${linked_file} ${cva6_sim_path} + + cd ${cva6_sim_path} + + python3 cva6.py --target cv32a60x --iss=$DV_SIMULATORS --iss_yaml=cva6.yaml \ + --c_tests $linked_file \ + --linker=../../config/gen_from_riscv_config/linker/link.ld \ + --gcc_opts="-static -mcmodel=medany -fvisibility=hidden -nostdlib \ + -nostartfiles -g ../tests/custom/common/syscalls.c \ + ../tests/custom/common/crt.S -lgcc \ + -I../tests/custom/env -I../tests/custom/common" + + +else + cp ${TOUR_PATH}/RoCCFragments.scala ${chipyardconfig_path}/fragments + cp ${TOUR_PATH}/RocketConfigs.scala ${chipyardconfig_path} + cp -r out_rocc/systemverilog/. ${rocketmain_path}/resources/vsrc + cp ${TOUR_PATH}/LazyRoCC.scala ${rocketmain_path}/scala/tile + cp ${adf_path} ${ROCC_PATH}/tests + + sed 's/RocketConfig/SmallrocketConfig/' ${ROCC_PATH}/variables.mk + + oacc-riscv ${oacc_flags} -c --mattr "+c,+m" -r -a ${adf_path} -o ${ROCC_PATH}/tests/compiled.o ${c_programs} + + cd ${ROCC_PATH} + source ./env.sh + cd tests + + riscv64-unknown-elf-gcc -L$HOME/local/lib -static -T htif.ld ${ROCC_PATH}/tests/compiled.o -o ${ROCC_PATH}/tests/crc.riscv + cd ../sims/verilator + make CONFIG=SmallrocketConfig run-binary BINARY=../../tests/crc.riscv + +fi + +exit 0 \ No newline at end of file diff --git a/testsuite/systemtest/scripts/coproGen_tour/coprogen.adf b/testsuite/systemtest/scripts/coproGen_tour/coprogen.adf new file mode 100644 index 000000000..f7fb2ceb2 --- /dev/null +++ b/testsuite/systemtest/scripts/coproGen_tour/coprogen.adf @@ -0,0 +1,1362 @@ + + + + + + + 32 + + + + + + + + sign + 0 + + + + + 32 + + + + + + + + sign + 0 + + + + + 32 + + + + + + + + sign + 0 + + + + + 32 + + + + + + + + sign + 1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B3 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B3 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B3 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + ALU_i1 + 32 + + + + + ALU_i2 + 32 + + + ALU_o1 + 32 + + + add + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + sub + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + and + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + ior + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + xor + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shr + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shru + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shl + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + lt + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + ltu + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + + + + + LSU_i1 + 32 + + + + + LSU_i2 + 32 + + + LSU_o1 + 32 + + + LSU_i3 + 32 + + + ald16 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + ald32 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + ald8 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + aldu8 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + aldu16 + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + ast32 + in1t + in2 + in3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + ast16 + in1t + in2 + in3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + ast8 + in1t + in2 + in3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + data + + + + + MUL_DIV_i1 + 32 + + + + + MUL_DIV_i2 + 32 + + + MUL_DIV_o1 + 32 + + + mul + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + mulhi + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + mulhisu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + mulhiu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + div + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + divu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + rem + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + remu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + + + + + STDOUT_i1 + 8 + + + + + stdout + in1t + + + 0 + 1 + + + + + + + + + FU_i1 + 32 + + + + + FU_i2 + 32 + + + FU_o1 + 32 + + + crc1 + P1 + P3 + + + 0 + 1 + + + 0 + 1 + + + + + gtu + P1 + P2 + P3 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + + + + normal + 32 + 32 + 2 + 1 + + RF_i1 + + + RF_o1 + + + RF_o2 + + 1 + + + + 8 + 0 + 65536 + + + + 8 + 65540 + 131071 + 1 + + + + + GCU_i1 + 32 + + + + + GCU_i2 + 32 + + + GCU_i3 + 32 + + + GCU_apc_o1 + 32 + + + GCU_ra_o1 + GCU_ra_i1 + 32 + + ra + + jump + pc + + + 0 + 1 + + + + + call + pc + + + 0 + 1 + + + + + callr + pc + + + + beqr + pc + in + in2 + + + + bner + pc + in + in2 + + + + bltr + pc + in + in2 + + + + bltur + pc + in + in2 + + + + calla + pc + in + + + + apc + pc + out + + + + bger + pc + in + in2 + + + + bgeur + pc + in + in2 + + + instructions + 2 + 1 + + + + add + sub + xor + or + and + sll + srl + sra + slt + sltu + div + divu + mul + mulhsu + mulh + mulhu + remu + rem + gtu + + + + addi + xori + ori + andi + slli + srli + srai + slti + sltiu + lb + lh + lw + lbu + lhu + jalr + + + + sw + sh + sb + + + + beq + bne + blt + bge + bltu + bgeu + + + + lui + auipc + + + + jal + + + + crc1 + + + + stdout + + +