diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b3eb4286a..175e66a54f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: pull_request: branches: - main + workflow_dispatch: jobs: build: @@ -60,7 +61,8 @@ jobs: cmake \ graphviz \ bc \ - ghdl + ghdl \ + iverilog - name: run: | diff --git a/.gitignore b/.gitignore index b1508c658f..b652e30dc8 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ openasip/scripts/tce-selftest openasip/src/applibs/LLVMBackend/passes/.dirstamp openasip/src/applibs/LLVMBackend/passes/buildllvmpass openasip/src/bintools/Assembler/tceasm +openasip/src/bintools/TestGenerator/generatetests openasip/src/bintools/BEMGenerator/createbem openasip/src/bintools/BEMViewer/viewbem openasip/src/bintools/BlocksDisassembler/tpef2pasm diff --git a/openasip/configure.ac b/openasip/configure.ac index 10b9a7de09..6ae073eb89 100644 --- a/openasip/configure.ac +++ b/openasip/configure.ac @@ -1387,6 +1387,7 @@ AC_CONFIG_FILES([ src/bintools/BEMViewer/Makefile src/bintools/Assembler/Makefile src/bintools/Disassembler/Makefile + src/bintools/TestGenerator/Makefile src/bintools/BlocksTranslator/Makefile src/bintools/BlocksDisassembler/Makefile src/applibs/Scheduler/Selector/Makefile @@ -1425,6 +1426,7 @@ AC_CONFIG_FILES([ src/applibs/EPSGenerator/Makefile src/applibs/costdb/Makefile src/applibs/program/Makefile + src/applibs/TestGenerator/Makefile src/codesign/Estimator/Makefile src/procgen/HDB/Makefile src/procgen/ProGe/Makefile diff --git a/openasip/data/Makefile.am b/openasip/data/Makefile.am index ab7f2e265e..8392b03fed 100644 --- a/openasip/data/Makefile.am +++ b/openasip/data/Makefile.am @@ -39,6 +39,9 @@ nobase_data_DATA = $(srcdir)/ProDe/confschema.xsd \ $(srcdir)/ProGe/debugger/*.vhdl.tmpl \ $(srcdir)/ProGe/*.snippet \ $(srcdir)/ProGe/debugger/tb/*.vhdl \ - $(srcdir)/riscv/* + $(srcdir)/riscv/* \ + $(srcdir)/RFGen/vhdl/*.vhdl \ + $(srcdir)/RFGen/verilog/*.v + EXTRA_DIST = ${nobase_data_DATA} diff --git a/openasip/data/ProGe/idecompressor.v.tmpl b/openasip/data/ProGe/idecompressor.v.tmpl index 148ca7b39e..8f0a335ea1 100644 --- a/openasip/data/ProGe/idecompressor.v.tmpl +++ b/openasip/data/ProGe/idecompressor.v.tmpl @@ -25,12 +25,12 @@ // 2012-04-04 1.0 Vinogradov ////////////////////////////////////////////////////////////////////////////// -`timescale 10ns/1ns module ENTITY_STR_decompressor -#( -`include "ENTITY_STR_globals_pkg.vh" -, +#( `include "ENTITY_STR_imem_mau_pkg.vh" +, +`include "ENTITY_STR_globals_pkg.vh" + ) ( output fetch_en, diff --git a/openasip/data/ProGe/ifetch.v.tmpl b/openasip/data/ProGe/ifetch.v.tmpl index 5fb77751b5..7519a37e30 100644 --- a/openasip/data/ProGe/ifetch.v.tmpl +++ b/openasip/data/ProGe/ifetch.v.tmpl @@ -25,14 +25,13 @@ // 2012-04-04 1.0 Vinogradov ////////////////////////////////////////////////////////////////////////////// -`timescale 10ns/1ns module ENTITY_STR_ifetch #( +`include "ENTITY_STR_imem_mau_pkg.vh" +, `include "ENTITY_STR_globals_pkg.vh" , `include "gcu_opcodes_pkg.vh" -, -`include "ENTITY_STR_imem_mau_pkg.vh" ) ( // program counter in @@ -76,6 +75,7 @@ module ENTITY_STR_ifetch integer reset_cntr; reg reset_lock; + reg mem_en_lock_r; parameter IFETCH_DELAY=1; @@ -89,28 +89,30 @@ module ENTITY_STR_ifetch assign ra_out= return_addr_reg; assign fetchblock = instruction_reg; - assign lock = ~fetch_en | busy; + assign lock = ~fetch_en | busy | mem_en_lock_r; always@(posedge clk or negedge rstx) - if(~rstx) - begin + if (~rstx) begin + mem_en_lock_r <= '1; + end else begin + mem_en_lock_r <= '0; + end + + always@(posedge clk or negedge rstx) + if (~rstx) begin pc_reg <= 0; pc_prev_reg <= {IMEMADDRWIDTH{1'b0}}; return_addr_reg <= {IMEMADDRWIDTH{1'b0}}; instruction_reg <= {IMEMWIDTHINMAUS*IMEMMAUWIDTH{1'b0}}; reset_cntr <= 0; reset_lock <= 1'b1; - end - else - begin - if( fetch_en && ~lock) - begin + end else begin + if (fetch_en && ~lock) begin pc_reg <= next_pc; pc_prev_reg <= pc_reg; end - if(~lock) - begin + if (~lock) begin if( reset_cntr < IFETCH_DELAY ) reset_cntr <= reset_cntr + 1; else @@ -124,7 +126,7 @@ module ENTITY_STR_ifetch if(ra_load) return_addr_reg <= ra_in; else - if(pc_load && pc_opcode==IFE_CALL) + if (pc_load && pc_opcode==IFE_CALL) // return address transformed to same form as all others addresses // provided as input return_addr_reg <= increased_pc; @@ -134,9 +136,9 @@ module ENTITY_STR_ifetch assign increased_pc = pc_reg + IMEMWIDTHINMAUS; always@(*) - if(pc_load) + if (pc_load) next_pc = pc_in; - else// no branch + else // no branch next_pc = increased_pc; endmodule diff --git a/openasip/data/ProGe/tb/clkgen.v b/openasip/data/ProGe/tb/clkgen.v new file mode 100644 index 0000000000..aa26260a6b --- /dev/null +++ b/openasip/data/ProGe/tb/clkgen.v @@ -0,0 +1,64 @@ +// Copyright (c) 2002-2024 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. +//////////////////////////////////////////////////////////////////////////////- +// Title : Clock generator +//////////////////////////////////////////////////////////////////////////////- +// File : clkgen.v +// Author : Joonas Multanen +// Company : +// Created : 2024-03-22 +// Last update: 2024-03-22 +//////////////////////////////////////////////////////////////////////////////- +// Description: A 50/50 testbench clock. The clock period is defined by +// a generic PERIOD +//////////////////////////////////////////////////////////////////////////////- +// Revisions : +// Date Version Author Description +// 2024-03-22 1.0 multanej Created +//////////////////////////////////////////////////////////////////////////////- + +module clkgen +#( + parameter PERIOD = 10 +) +( + output wire clk, + input wire en +); + + reg clk_internal; + +initial begin + clk_internal <= 0; +end + +always begin + if (en) begin + #(PERIOD) clk_internal <= ~clk_internal; + end else begin + clk_internal <= 0; + end +end + +assign clk = clk_internal; + +endmodule // clkgen diff --git a/openasip/data/ProGe/tb/imem_arbiter.v b/openasip/data/ProGe/tb/imem_arbiter.v index d567ff1a7f..318257c885 100644 --- a/openasip/data/ProGe/tb/imem_arbiter.v +++ b/openasip/data/ProGe/tb/imem_arbiter.v @@ -24,7 +24,6 @@ // Revisions : // 2012-04-04 1.0 Vinogradov ////////////////////////////////////////////////////////////////////////////// -`timescale 10ns/1ns module imem_arbiter #( diff --git a/openasip/data/ProGe/tb/legacy_testbench.v.tmpl b/openasip/data/ProGe/tb/legacy_testbench.v.tmpl index 9979002ef2..cf133666f3 100755 --- a/openasip/data/ProGe/tb/legacy_testbench.v.tmpl +++ b/openasip/data/ProGe/tb/legacy_testbench.v.tmpl @@ -31,7 +31,6 @@ // Revisions : // 2012-04-04 1.0 Vinogradov ////////////////////////////////////////////////////////////////////////////// -`timescale 1ns/1ns module testbench #( diff --git a/openasip/data/ProGe/tb/mem_arbiter.v b/openasip/data/ProGe/tb/mem_arbiter.v index 34fa3c77c0..89d797e258 100644 --- a/openasip/data/ProGe/tb/mem_arbiter.v +++ b/openasip/data/ProGe/tb/mem_arbiter.v @@ -24,7 +24,6 @@ // Revisions : // 2012-04-04 1.0 Vinogradov ////////////////////////////////////////////////////////////////////////////// -`timescale 10ns/1ns module mem_arbiter #( diff --git a/openasip/data/ProGe/tb/synch_byte_mask_sram.v b/openasip/data/ProGe/tb/synch_byte_mask_sram.v new file mode 100644 index 0000000000..d8633ad724 --- /dev/null +++ b/openasip/data/ProGe/tb/synch_byte_mask_sram.v @@ -0,0 +1,79 @@ +module synch_byte_mask_sram #(parameter + // pragma translate_off + init = 1, + INITFILENAME = "ram_init", + trace = 1, + TRACEFILENAME = "dpram_trace", + trace_mode = 0, + access_trace = 1, + ACCESSTRACEFILENAME = "access_trace", + // pragma translate_on + DATAW = 32, + ADDRW = 7) +( + input wire clk, + input wire [DATAW-1:0] adata, + input wire [ADDRW-1:0] aaddr, + input wire avalid, + input wire awren, + input wire [(DATAW/8)-1:0] astrb, + output wire aready, + output wire rvalid, + input wire rready, + output wire [DATAW-1:0] rdata +); + +parameter DW = DATAW; +parameter AW = ADDRW; +parameter DW8 = DW / 8; + +reg [DW-1:0] mem_r [0:2**AW-1]; +reg [DW-1:0] q_r; +reg [DW-1:0] wr_mask; + +integer line; + +integer k; + +//this initial path can be synthesize by quartus + initial + if(init) + if(INITFILENAME!="") + begin + $readmemb(INITFILENAME,mem_r); + $display("Memory initialized from file %s",INITFILENAME); + end + else + begin + for(k=0; k<2**ADDRW; k=k+1) + mem_r[k]={DATAW{1'b0}}; + $display("Memory initialized to zeroes!"); + end + + +assign line = aaddr; + +always @(posedge clk) begin + if (avalid && awren) begin + mem_r[line] <= (adata & wr_mask) | (mem_r[line] & ~wr_mask); + q_r <= (adata & wr_mask) | (mem_r[line] & ~wr_mask); + end else if (avalid) begin + q_r <= mem_r[line]; + end +end + +integer i,j; +always @* begin + wr_mask = 0; + for (i = 0; i < DW8; i = i + 1) begin + for (j = i * 8; j < i * 8 + 8; j = j + 1) begin + wr_mask[j] = astrb[i]; + end + end +end + +assign rdata = q_r; +assign rvalid = 1; +assign aready = 1; + +endmodule diff --git a/openasip/data/ProGe/tb/synch_dualport_sram.v b/openasip/data/ProGe/tb/synch_dualport_sram.v index cf99df6f01..d1cd408436 100644 --- a/openasip/data/ProGe/tb/synch_dualport_sram.v +++ b/openasip/data/ProGe/tb/synch_dualport_sram.v @@ -34,7 +34,6 @@ // 2012-04-04 1.0 Vinogradov ////////////////////////////////////////////////////////////////////////////// -`timescale 10ns/1ns module synch_dualport_sram #( diff --git a/openasip/data/ProGe/tb/synch_sram.v b/openasip/data/ProGe/tb/synch_sram.v index 980e19d7ba..7e0bb7dc23 100644 --- a/openasip/data/ProGe/tb/synch_sram.v +++ b/openasip/data/ProGe/tb/synch_sram.v @@ -32,16 +32,17 @@ // Revisions : // 2012-04-04 1.0 Vinogradov ////////////////////////////////////////////////////////////////////////////// -`timescale 10ns/1ns module synch_sram #( - parameter init =1, - parameter INITFILENAME ="ram_init", - parameter trace =1, + parameter init = 1, + parameter INITFILENAME = "ram_init", + parameter trace = 1, parameter TRACEFILENAME = "dpram_trace", // trace_mode 0: hex, trace_mode 1: integer, trace_mode 2: unsigned parameter trace_mode = 0, + parameter access_trace = 1, + parameter ACCESSTRACEFILENAME = "access_trace", parameter DATAW = 32, parameter ADDRW = 7 ) diff --git a/openasip/data/ProGe/tb/testbench.v.tmpl b/openasip/data/ProGe/tb/testbench.v.tmpl new file mode 100755 index 0000000000..7f474b81dd --- /dev/null +++ b/openasip/data/ProGe/tb/testbench.v.tmpl @@ -0,0 +1,125 @@ +// Copyright (c) 2002-2024 Tampere University. +// Copyright (c) 2012 Vinogradov Viacheslav +// +// 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. +// Description: architecture for processor with a core with single port data +// memory/cache (structural) and with a core with dual-port data memory/cache +// (structural_dp_dmem) +////////////////////////////////////////////////////////////////////////////// +// Title : testbench for TTA processor +////////////////////////////////////////////////////////////////////////////// +// Description: Simply resets the processor and triggers execution +////////////////////////////////////////////////////////////////////////////// +// Revisions : +// 2012-04-04 1.0 Vinogradov +// 2024-03-14 1.1 multanej +////////////////////////////////////////////////////////////////////////////// + +module testbench +#( +`include "ENTITY_STR_imem_mau_pkg.vh" +, +`include "ENTITY_STR_globals_pkg.vh" +) + (); + + reg clk; + reg rstx; + wire rstx_wire; + reg enable_clock; + integer execution_count_reg; + wire [0:0] lock_status_wire; + integer execution_count_limit; + integer exec_count_file; + integer scan_file; + + // Stop simulation after `SIMTIME, if defined + `ifdef SIMTIME + initial begin + #(`SIMTIME + PERIOD) $finish; + end + `endif + + initial + begin + enable_clock = 1; + end + + initial + begin + exec_count_file = $fopen("execution_limit", "r"); + if (exec_count_file != 0) begin + // execution_limit should contain a single line with the execution count. + scan_file = $fscanf(exec_count_file, "%d", execution_count_limit); + wait(execution_count_reg == execution_count_limit) + $finish; + end + + rstx = 1'b0; + #(PERIOD*2 + PERIOD/10); + rstx = 1'b1; + // Test runs until RUNTIME, which is equal to SIMTIME given by + // iverilog_compile script. Reduced by 20ns to match vhdl simulation time. + //#(RUNTIME-20); + // Test runs until requested amount of instructions have been executed or + // stopped by simulation script. + end // initial begin + + assign rstx_wire = rstx; + + // purpose: Counts executed instructions per core + // type : sequential + // inputs : clk, rstx + // outputs: execution_count_reg + always @(posedge clk) begin + if (rstx == 0) begin + execution_count_reg = 0; + end else begin + if (lock_status_wire[0] == 'b0) begin + execution_count_reg += 1; + end + end + end + + + initial begin + wait(enable_clock == 1); + clk <= 1'b1; + forever begin + #(PERIOD/2) clk <= ~clk; + end + end + + // purpose: Controls clock generation. At beginning of the simulation the + // clock is enabled. If execution limit is defined then the clock + // will be stopped causing simulation to end. + // type : combinational + // inputs : execution_count_reg + // outputs: enable_clock + + proc dut( + .clk ( clk), + .rstx ( rstx_wire), + + .locked (lock_status_wire) + ); + +endmodule diff --git a/openasip/data/ProGe/tce_util_pkg.vh b/openasip/data/ProGe/tce_util_pkg.vh index bd2efbda06..6162bd3306 100644 --- a/openasip/data/ProGe/tce_util_pkg.vh +++ b/openasip/data/ProGe/tce_util_pkg.vh @@ -184,9 +184,13 @@ function integer bit_width; input integer value; begin - value=value-1; - for (bit_width=0; value>0; bit_width=bit_width+1) + if (value == 1) begin + bit_width = 1; + end else begin + value=value-1; + for (bit_width=0; value>0; bit_width=bit_width+1) value = value>>1; + end end endfunction @@ -198,4 +202,4 @@ flip_bits[i]=flipped_vec[dataw-i-1]; end endfunction - \ No newline at end of file + diff --git a/openasip/data/RFGen/verilog/rf_dump_snippet.v b/openasip/data/RFGen/verilog/rf_dump_snippet.v new file mode 100644 index 0000000000..c69a0c8ba2 --- /dev/null +++ b/openasip/data/RFGen/verilog/rf_dump_snippet.v @@ -0,0 +1 @@ +// Verilog version of RF dump snippet not yet implemented! diff --git a/openasip/data/RFGen/vhdl/rf_dump_snippet.vhdl b/openasip/data/RFGen/vhdl/rf_dump_snippet.vhdl new file mode 100644 index 0000000000..a2c1cc79a7 --- /dev/null +++ b/openasip/data/RFGen/vhdl/rf_dump_snippet.vhdl @@ -0,0 +1,103 @@ + --pragma translate_off + file_output : process + file rf_trace : text; + variable line_out : line; + variable start : boolean := true; + constant seperator : character := '|'; + constant dash : character := '-'; + variable opc : integer := 0; + + function ceil4 ( + constant val : natural) + return natural is + begin -- function ceil4 + return natural(ceil(real(val)/real(4)))*4; + end function ceil4; + + function ext_to_multiple_of_4 ( + constant slv : std_logic_vector) + return std_logic_vector is + begin + return std_logic_vector(resize( + unsigned(slv), ceil4(slv'length))); + end function ext_to_multiple_of_4; + + function to_unsigned_hex ( + constant slv : std_logic_vector) return string is + variable resized_slv : std_logic_vector(ceil4(slv'length)-1 downto 0); + variable result : string(1 to ceil4(slv'length)/4) + := (others => ' '); + subtype digit_t is std_logic_vector(3 downto 0); + variable digit : digit_t := "0000"; + begin + resized_slv := ext_to_multiple_of_4(slv); + for i in result'range loop + digit := resized_slv( + resized_slv'length-((i-1)*4)-1 downto resized_slv'length-(i*4)); + case digit is + when "0000" => result(i) := '0'; + when "0001" => result(i) := '1'; + when "0010" => result(i) := '2'; + when "0011" => result(i) := '3'; + when "0100" => result(i) := '4'; + when "0101" => result(i) := '5'; + when "0110" => result(i) := '6'; + when "0111" => result(i) := '7'; + when "1000" => result(i) := '8'; + when "1001" => result(i) := '9'; + when "1010" => result(i) := 'a'; + when "1011" => result(i) := 'b'; + when "1100" => result(i) := 'c'; + when "1101" => result(i) := 'd'; + when "1110" => result(i) := 'e'; + when "1111" => result(i) := 'f'; + -- For TTAsim bustrace compatibility + when others => + result := (others => '0'); + return result; + end case; + end loop; -- i in result'range + return result; + end function to_unsigned_hex; + + function reg_to_alias ( + constant index : integer) return string is + variable result: string(1 to 3); + begin + case index is + when 0 => result := "zr "; + when 1 => result := "ra "; + when 2 => result := "sp "; + when 3 => result := "gp "; + when 4 => result := "tp "; + when 5 => result := "t0 "; + when 6 => result := "t1 "; + when 7 => result := "t2 "; + when 8 => result := "s0 "; + when 9 => result := "s1 "; + when 10 => result := "a0 "; + when 11 => result := "a1 "; + when 12 => result := "a2 "; + when 13 => result := "a3 "; + when 14 => result := "a4 "; + when 15 => result := "a5 "; + when 16 => result := "a6 "; + when 17 => result := "a7 "; + when 18 => result := "s2 "; + when 19 => result := "s3 "; + when 20 => result := "s4 "; + when 21 => result := "s5 "; + when 22 => result := "s6 "; + when 23 => result := "s7 "; + when 24 => result := "s8 "; + when 25 => result := "s9 "; + when 26 => result := "s10"; + when 27 => result := "s11"; + when 28 => result := "t3 "; + when 29 => result := "t4 "; + when 30 => result := "t5 "; + when 31 => result := "t6 "; + when others => result := "???"; + end case; + return result; + end function reg_to_alias; diff --git a/openasip/data/TeGe/testrunner.py b/openasip/data/TeGe/testrunner.py new file mode 100755 index 0000000000..82ab2acf9f --- /dev/null +++ b/openasip/data/TeGe/testrunner.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2002-2011 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. +# +# Script that runs test cases primaly generated by test generator in rtlsim. +# +# Reads test case files from INPUT directory, runs them and outputs results +# under OUTPUT directory. +# +# INPUT is by default: "PROGEOUTPUT/tests". The variable is overriden by option +# -i, --input +# OUTPUT is by default: "PROGEOUTPUT/test-results". The variable is overriden +# by option -o, --output +# PROGEOUTPUT is by default: ".". The variable is overriden by option +# -x, --hdl-dir +# +# A single test case consists of files: +# - a bustrace: .bustrace +# - a instruction image .img +# - data images _.img +# These files must be present for each to be accepted. +# The bustrace is used to determine run time of the program and in +# verification. +# +# Automatically recognizes available RTL-simulator if they are in PATH. The +# simulators to search are GHDL and ModelSim. +# + +import os, sys, optparse, collections, re, glob, subprocess, shutil, difflib +from os import path +from os import listdir +from optparse import OptionParser +from collections import OrderedDict +from subprocess import Popen + +#TODO? should not actually delete old test results? May be only delete +# results of test cases to be run. +#TODO? Recognize hdl_sim_stdout.txt and use it to determine success of +# test case. + +# Named tuple type of a single test case +Testcase = collections.namedtuple('Testcase', + [ 'testname', + 'bustracefile', + 'runtime', + 'instr_image_file', + 'data_image_files' ]); + +class InitError(Exception): + def __init__(self, value): + self.value = value + + def __str__(self): + return repr(self.value) + +def setupOptionParser(): + """Initializes option parser.""" + + usage = "usage: %prog [options]." + optParser = optparse.OptionParser(usage) + optParser.add_option('-i', '--input', + action="store", metavar="DIR", + dest="input_dir", default="./tests", + help="Path to directory containing test cases to"\ + " be run. [default: %default]") + optParser.add_option('-o', '--output', + action="store", metavar="DIR", + dest="output_dir", default="./test-results", + help="Path to put results of run test cases. "\ + "The contents of the directory will be erased. "\ + "[default: %default]") + optParser.add_option('-x', '--hdl-dir', + action="store", metavar="DIR", + dest="proge_dir", default=".", + help="Path to directory where ProGe created HDL "\ + "files. [default: %default]") + optParser.add_option('-t', '--testcase', + action="append", metavar="TESTCASE", + dest="selected_testcases", default=None, + help="Runs only the specified test case by name. ") + sim_choices = ['auto', 'modelsim', 'ghdl'] + optParser.add_option('-s', '--simulator', + action="store", dest="simulator", + choices=sim_choices, + default='auto', + help="The simulator to be used in test runs. " + + "The choices are " + str(sim_choices) + ". " + + "[default: %default] ") + #TODO add simulator select (ghdl, modelsim, [auto]) + #TODO add verbosity option to tell phases the script + return optParser + + +def valid_options(options, args): + """Checks the given or default option values are valid.""" + if not path.isdir(options.input_dir): + print("Error: no such input directory: %s" % options.input_dir) + return False + if not path.isdir(options.proge_dir): + print("Error: no such hdl directory: %s" % options.proge_dir) + return False + return True + + +def mkdirp(dir): + """ + Emulates mkdir -p: creates directory including intermediate ones if + not already existing. + """ + if not path.exists(dir): + # Use absolute path since makedirs may become confused of '..'es + os.makedirs(path.abspath(dir)) + + +def number_of_lines(filepath): + return sum(1 for l in open(filepath)) + + +def get_rtl_bustrace(progeoutdir): + """ + Returns bustrace file name produced by RTL simulation. Returns + None if the file is not found. + """ + bustraces = glob.glob(path.join(progeoutdir, 'core0_execbus.dump')) + if len(bustraces) > 0: + return bustraces[0] + bustraces = glob.glob(path.join(progeoutdir, 'execbus.dump')) + if len(bustraces) > 0: + return bustraces[0] + return None + + +def diff_bustrace(ref_bustrace, new_bustrace): + """ + Return True if the given bustraces differs. + ref_bustrace is treated as reference file. + """ + diff_content = None + diff_status = False + try: + ref_file = open(ref_bustrace, 'r') + ref_str = ref_file.readlines() + new_file = open(new_bustrace, 'r') + num_of_lines = len(ref_str) + new_str = new_file.readlines()[:num_of_lines] + diff_content = [ line for line in difflib.unified_diff(ref_str, + new_str, + ref_bustrace, + new_bustrace) ] + diff_status = len(diff_content) != 0; + except Exception as e: +# print("Exception in diff_bustrace():", e #debug) + return (True, None) + + return (diff_status, diff_content) + + +def get_testcases(options): + """ + Collects valid testcases from input directory + Returns list of named tuple Testcase. + """ + inputdir = options.input_dir + files = [ f for f in listdir(inputdir) + if path.isfile(path.join(inputdir, f)) ] + re_bustrace = re.compile(r'[\w-]+\.bustrace', re.IGNORECASE) + cases = [ f for f in files[:] if re_bustrace.match(f) ] + cases = [ f.replace('.bustrace', '') for f in cases[:] ] + test_case_list = [] + for case in cases[:]: + bustrace = path.join(inputdir, case + '.bustrace') + instr_img = path.join(inputdir, case + '.img') + if not path.isfile(instr_img) or not os.access(instr_img, os.R_OK): + continue + if not path.isfile(bustrace) or not os.access(bustrace, os.R_OK): + continue + data_imgs = [ path.join(inputdir, f) for f in listdir(inputdir) + if re.match(case + r'_\w*\.img', f) + and path.isfile(path.join(inputdir, f)) + and os.access(path.join(inputdir, f), os.R_OK)] + + test_case_list.append(Testcase(testname=case, + bustracefile=bustrace, + runtime=number_of_lines(bustrace), + instr_image_file=instr_img, + data_image_files=data_imgs)) + + # Sort by test name deterministic test run. + test_case_list.sort(key=lambda tc: tc[0]) + if options.selected_testcases != None: + return [ tc for tc in test_case_list[:] + if tc.testname in options.selected_testcases ] + else: + return test_case_list + + +def command_exists(cmd_name): + """ + Returns true if the given command name can be invoked. Otherwise, returns + false. + """ + path_locations = os.environ.get("PATH").split(os.pathsep) + for location in path_locations: + if path.isfile(path.join(location, cmd_name)): + return True + return False; + + +def select_rtl_simulator(choice): + """ + Returns runnable RTL simulator or None if the choice not available. + The choices are auto, modelsim and ghdl. The choice auto select one of the + latter. + """ + # Ordered list of cadidates. 'simulator name': 'executable' + rtlsim_choices = OrderedDict([('modelsim', 'vsim'), + ('ghdl', 'ghdl') ]) + if choice == "auto": + for rtl_simulator in rtlsim_choices: + if command_exists(rtlsim_choices[rtl_simulator]): + return rtl_simulator + elif choice in rtlsim_choices: + if command_exists(rtlsim_choices[choice]): + return choice + return None + + +def run_command(cmdString): + """ + Runs a shell command silently and without providing any input. + Returns exit code of the shell command. + """ + devNull = open(os.devnull, 'w') +# print("Running shell cmd: " + cmdString #debug) + process = Popen(cmdString, shell=True, stdout=devNull, stderr=devNull, + close_fds=False) + if process == None: + print("Error: Could not create shell process.") + sys.exit(2) + process.communicate() + return process.returncode + + +def rtl_compile_script(rtl_simulator): + """ + Returns the name of the compilation script for given rtl simulator + """ + if rtl_simulator == "modelsim": + return "modsim_compile.sh" + elif rtl_simulator == "ghdl": + return "ghdl_compile.sh" + else: + raise LookUpError("Unknown RTL simulator Type.") + + +def rtl_simulate_script(rtl_simulator): + """ + Returns the name of the compilation script for given rtl simulator + """ + if rtl_simulator == "modelsim": + return "modsim_simulate.sh" + elif rtl_simulator == "ghdl": + return "ghdl_simulate.sh" + else: + raise LookUpError("Unknown RTL simulator Type.") + + +def prepare_testbench(progeoutdir, rtl_simulator): + """ + Initialized HDL test bench for running test cases. + """ + simCommand = "cd %s; " % progeoutdir + simCommand += "./" + rtl_compile_script(rtl_simulator) + if rtl_simulator == "modelsim": + simCommand += " -c; " + return run_command(simCommand) + + +def get_data_address_space_name(testcase, data_image_file): + """ + Extracts data address space name from data image file path. + """ + result = path.basename(data_image_file) + result = result.replace(testcase.testname + '_', ''); + result = result.replace('.img', ''); + return result + +def load_images(progeoutdir, testcase): + """ + Load instruction and data images for RTL simulation. + """ + try: + shutil.copyfile(testcase.instr_image_file, + path.join(progeoutdir, 'tb/imem_init.img')) + except IOError as e: + raise IOError("Error: Could not load instruction image: " + e) + if len(testcase.data_image_files) > 0: + # Note: test bench does not support 1+ data memories yet + try: + for data_image_file in testcase.data_image_files: + addr_space_name = get_data_address_space_name(testcase, + data_image_file) + shutil.copyfile(data_image_file, + path.join(progeoutdir, + 'tb/dmem_' + + addr_space_name + + '_init.img')) + except IOError as e: + raise IOError("Error: Could not load data image: " + e) + else: + # create dummy data image since test bench does not expect non-existent + # file. + try: + open(path.join(progeoutdir, 'tb/dmem_init.img'), 'w').close() + except IOError as e: + raise IOError("Error: Could not load data image: " + e) + +def run_simulation(progeoutdir, rtl_simulator, + num_of_instructions, timeout=1000000000): + """ + Runs simulation by given number of instructions timeouted by timeout in + nanoseconds. + """ + simCommand = "cd %s; " % progeoutdir + simCommand += "./" + rtl_simulate_script(rtl_simulator) + " " + if rtl_simulator == "modelsim": + simCommand += "-c " + simCommand += "-i %d -r %d; " % (num_of_instructions, timeout); + return run_command(simCommand) == 0 + + +def run_testcase(testcase, rtl_simulator, progeoutdir, outputdir): + """ + Runs the given test case in RTL-simulator. + Assumes that test bench is prepared before calling this. + Returns tuple of (success status, reason) + """ + #TODO do cleanup + + load_images(progeoutdir, testcase) + + # Directory where test case specific results are placed + tc_result_dir = path.join(outputdir, testcase.testname) + + reason = None + success = run_simulation(progeoutdir, rtl_simulator, testcase.runtime) + if not success: + reason = "Simulation error" + + if success: + ref_file = testcase.bustracefile + assert path.isfile(ref_file) + new_file = get_rtl_bustrace(progeoutdir) + if new_file == None: + success = False + reason = "No bustrace" + else: + differs, diff = diff_bustrace(ref_file, new_file) + if differs: + success = False + reason = "Bustrace mismatch" + mkdirp(tc_result_dir) + diff_file = open(path.join(tc_result_dir, "bustrace.diff"), + 'w') + diff_file.writelines(diff) + diff_file.close() + + # Save dump files if the test case fails for further inspection + if not success: + mkdirp(tc_result_dir) + for dumpfile in glob.glob(path.join(progeoutdir, "*.dump")): + try: + shutil.copy(dumpfile, tc_result_dir) + except: + pass + + #TODO return status (OK, FAIL, TIMEOUT, ERROR) + return (success, reason) + + +def clear_simulation_files(progeoutdir): + """ + Clears simulation related files. + """ + dumpfiles = [ path.join(progeoutdir, f) for f in listdir(progeoutdir) + if path.isfile(path.join(progeoutdir, f)) and + re.match(r'.*\.dump', f, re.IGNORECASE) ] + for dumpfile in dumpfiles[:]: + os.remove(dumpfile); + +def test_result_format(testname, success, reason): + """ + Makes pretty test result string. + """ + status_str = "" + reason_str = "" + if success: + status_str = "OK" + else: + status_str = "FAIL" + reason_str = "(%s)" % reason + return "{0:>6} {1} {2}".format(status_str, testname, reason_str) + + +def run_testcases(progeoutdir, outputdir, list_of_testcases, rtl_simulator): + """ + Accepts list of named tuple Testcase. Runs each test case in + RTL-simulator and collects data from it. + """ + if prepare_testbench(progeoutdir, rtl_simulator) != 0: + print("Test bench compilation failed.") + return False + + num_failing = 0 + summary_file = open(path.join(outputdir, "summary.log"), 'a') + for testcase in list_of_testcases[:]: + try: + clear_simulation_files(progeoutdir) + success, reason = run_testcase(testcase, rtl_simulator, + progeoutdir, outputdir) + if not success: + num_failing += 1 + print(test_result_format(testcase.testname, success, reason)) + summary_file.write(test_result_format( + testcase.testname, success, reason) + '\n') + + except Exception as e: + print("caught exception:", e) + + #TODO compile coverage report + summary_file.close() + return num_failing + + + +def prepare_testrun_env(options): + """ + Makes checks and clean ups before running the test cases. + """ + def expect_proge_file(filepath): + if (not path.exists(path.join(options.proge_dir, filepath)) or + not path.isfile(path.join(options.proge_dir, filepath))): + raise InitError("Could not locate " + filepath + " in " + + options.proge_dir + " (hdl-dir)") + + expect_proge_file(rtl_compile_script( + select_rtl_simulator(options.simulator))) + expect_proge_file(rtl_simulate_script( + select_rtl_simulator(options.simulator))) + + mkdirp(options.output_dir) + clear_simulation_files(options.output_dir) + + # If the dir already existed and is non-empty, delete old test results. + assert path.isdir(options.output_dir) + for file_or_dir in listdir(options.output_dir): + file_or_dir = path.join(options.output_dir, file_or_dir) + if path.isfile(file_or_dir): + os.remove(file_or_dir) + elif path.isdir(file_or_dir): + shutil.rmtree(file_or_dir) + + +def main(): + """The main function.""" + optParser = setupOptionParser() + (options, args) = optParser.parse_args() + if not valid_options(options, args): + return 1 + testcases = get_testcases(options) + simulator = select_rtl_simulator(options.simulator) + if simulator == None: + print("No RTL simulator available.") + return 1 +# print("Using RTL-simulator: " + simulator) + try: + prepare_testrun_env(options) + except InitError as e: + print(e) + return 1 + num_failing = run_testcases(options.proge_dir, options.output_dir, \ + testcases, simulator) + # If num_failing == 0, evaluates to True + return num_failing + +if __name__ == '__main__': + sys.exit(main()) diff --git a/openasip/doc/man/OpenASIP/OpenASIP.tex b/openasip/doc/man/OpenASIP/OpenASIP.tex index 337099a40b..8f78cacffd 100644 --- a/openasip/doc/man/OpenASIP/OpenASIP.tex +++ b/openasip/doc/man/OpenASIP/OpenASIP.tex @@ -8005,6 +8005,285 @@ \subsection{Usage} % TODO: perhaps a simple example of the resulting dump in here? +\section{Test Generator (generatetests)} +\label{section:tege} + +\textbf{Test Generator} (TeGe) is a program to generate test cases for +the given architecture. + +\textbf{Input}: ADF, [IDF], [TPEF] + +\textbf{Output}: program and data images (.img), bus traces, reference +programs. + +\subsection{Usage} + +The usage of the \emph{generatetests} application is as follows: + +\begin{verbatim} +generatetests -a adffile +\end{verbatim} + +The \emph{adffile} is the ADF file. + +The possible options of the application are as follows:\\ + +\begin{tabular}{p{0.10\textwidth}p{0.15\textwidth} + p{0.65\textwidth}} + +\textbf{Short Name} &\textbf{Long Name} &\textbf{Description} \\ +\hline +a & adf & The target architecture for the generated tests. The option +is mandatory. \\ +i & idf & The Implementation definition for the architecture. The option is +optional but implicitly disables some test generators if left out.\\ +l & list-generators & Lists the test generators with their names, descriptions +and activation status.\\ +e & enable & Enables the test generators by names that are disabled by +default. The generator names are separated with commas. \\ +d & disable & Disables the test generators by names. The generator names are +separated with commas.\\ +D & disable-all & Disables all the test generators. This option can be +used with-e option to enable only certain tests conveniently. \\ +s & seed & Set seed value that is used in the test generators for +randomized tests. The option accepts number and string as input. +If the input is left out then new seed is generated and printed into +stdout. If the option is not specified then default seed value is used. \\ +o & output & The directory where the test files are placed. If option is not +specified nor the option \emph{hdl-dir} then the files are placed +under ./tests/ directory.\\ +x & hdl-dir & Directory root where ProGe generated HDL files and generates +the program and data image files. The test files are placed under +\emph{hdl-dir}/tb/tests directory.\\ +f & format & Output format of program and data images. Choices are 'ascii', +'array', 'mif', 'coe', 'vhdl' or 'binary'. Default is 'ascii' \\ +p & program & Additional user defined test programs (TPEF) to be included +in tests.\\ + +\end{tabular}\\ + +Test generator is a tool to generate various test case programs +automatically from the targeted architecture. The tests are generated +from the tool's test generator units which each is responsible to test +some area of the architecture. + +The tool outputs test case files consisting of program images, bus +traces of the test programs and/or reference code files (TPEF, disasm) +as ``original'' reference sources of the test cases. + +%TODO table of generated files + +The test generator creates random input values when it is generating +tests for operations of the architecture and assumes that the values +are valid. If the value generation needs to be restricted there is +couple way to control input generation in OSAL by: + +\begin{itemize} +\item inspecting the input values to be given to operation and + discarding the invalid ones, +\item overriding the random input generation and generating the custom input + instead. +\end{itemize} + +\subsubsection{Custom test input validation} + +Validation of inputs of a operation is done by writing input check +code between INPUT\_VALIDATION and END\_INPUT\_VALIDATION block. If +the given inputs are correct for the operation the DECLARE\_VALID +expression is used to accept the values. Below is example code of +input validation of SQRTF operation: + +\begin{verbatim} +OPERATION(SQRTF) + +TRIGGER + IO(2) = sqrtf(FLT(1)); +END_TRIGGER; + +INPUT_VALIDATION + if (FLT(1) >= 0.0f ) { // Check the input is positive. + DECLARE_VALID; // If so then accept the input. + } // Otherwise the input is discarded. +END_INPUT_VALIDATION + +END_OPERATION(SQRTF) +\end{verbatim} + +\subsubsection{Custom test input generation} + +To add custom test input values for a operation use +DEFINE\_TESTVECTORS block to define the test values (aka. ``test +vectors''). The custom test values are fed by defining values for each +input operand of the operation using IO and SUBWORD expressions and +then adding them with ADD\_TESTVECTOR expression. The following +example defines three test vectors: + +\begin{verbatim} +OPERATION(MAX) + +TRIGGER + SIntWord in1 = static_cast(INT(1)); + SIntWord in2 = static_cast(INT(2)); + SIntWord in3 = (in1 > in2) ? in1 : in2; + IO(3) = static_cast(in3); +END_TRIGGER; + +DEFINE_TESTVECTORS + IO(1) = 1; + IO(2) = -1; + ADD_TESTVECTOR; + IO(1) = -1; + IO(2) = 1; + ADD_TESTVECTOR; + IO(1) = -1; + IO(2) = -1; + ADD_TESTVECTOR; +END_DEFINE_TESTVECTORS + +END_OPERATION(MAX) +\end{verbatim} + +\subsubsection{examples of the usage} + +The following command is minimum example that generates tests for the +given architecture using default seed value and puts them under +\emph{tests} directory. + +\begin{verbatim} +> generatetests -a machine.adf +> ls tests/ +\end{verbatim} + +The generated tests can be stored to other directory with +\emph{output} and \emph{hdl-dir} options. The first of the following +examples writes test files into user defined directory and the second +example writes the files into \emph{hdl-dir}/tb/tests directory. + +\begin{verbatim} +> generatetests -a machine.adf -o other-directory +> ls other-directory/ +> generatetests -a machine.adf -x path-to-proge-dir +> ls path-to-proge-dir/tb/tests/ +\end{verbatim} + +Different seed value can be set with \emph{seed} option. The first +example uses given value as seed and the second example generates new +seed: + +\begin{verbatim} +> generatetests -a machine.adf -s 1234567 +> generatetests -a machine.adf -s +Generated seed: 109604828 +\end{verbatim} + +The test generators of the program can be viewed with +\emph{list-generators}. To enable and disable test generators during +test generation use \emph{enable} and \emph{disable} options. + +\begin{verbatim} +> generatetests -l +immediate-tests [enabled] + Generates various short and long immediate values. +operation-tests [enabled] + Generates tests for basic operations for each Function Unit. +... +> generatetests -e immediate-tests,operation-tests -a machine.adf +> generatetests -d immediate-tests,operation-tests -a machine.adf +\end{verbatim} + +The additional test code can be included with \emph{program} option: + +\begin{verbatim} +> generatetests -a machine.adf -p example1.tpef -p example2.tpef +> generatetests -a machine.adf -p example1.tpef,example2.tpef +\end{verbatim} + +\subsection{Test Runner Script} +\label{subsection:testrunner} + +The usage of the \emph{testrunner.py} script is as follows: + +\begin{verbatim} +./testrunner.py +\end{verbatim} + +This script is generated by test generator. The all possible optional +options of the script are as follows: + +\begin{tabular}{p{0.10\textwidth}p{0.15\textwidth} + p{0.65\textwidth}} + +\textbf{Short Name} &\textbf{Long Name} &\textbf{Description} \\ +\hline +i & input & +Specifies input directory where the test cases are searched from. +Default directory is '\textit{PROGEOUTPUT/tests}' where PROGEOUTPUT is +'.' or directory set by option -x. \\ + +o & output & +Specifies output directory where the results of test cases are placed +to. Default directory is '\textit{PROGEOUTPUT/test-results}' where PROGEOUTPUT +is '.' by default or directory set by option -x. \\ + +x & hdl-dir & +Specifies directory where a processor design is generated by ProGe. By +default the script is assumed to be placed in this directory and +therefore the option is not needed unless it is placed else +where. Default directory is tectit{PROGEOUTPUT} which is '.'.\\ + +t & testcase & +Instead of running all test cases from input directory this option +only runs a single test case by name. \\ + +s & simulator & +Specifies RTL simulator to used in RTL simulation. Choices are +\textit{modelsim}, \textit{ghdl} and \textit{auto}. When the choice is +\textit{auto} the simulator is selected automatically. Default +choice is \textit{auto}. \\ + +\end{tabular} \\ + +Test runner script is generated by test generator tool (section +\ref{section:tege}) and is used to run the generated tests in a +processor design. The script reads test cases from input directory, +runs them in RTL simulator and reports results in terminal screen and +output directory. + +The test cases files needed in simulation run are recognized by their +extension and these file names without file extension are treated as +test case names. The runner script expects to find the test case files +for a single test case by patterns of \textit{.bustrace}, +\textit{.img} and +\textit{\_.img}. + +The first file contains reference bus trace which is compared to one +produced in RTL simulation. If the bus traces matches a test case is +treated as passed and otherwise treated as failure. The second file +contains instruction image of the test case and the last file(s) +contains data images for each address space by name. At minimum the +test runner script needs a bus trace and instruction image of a test +case must be found, otherwise the test case is ignored. + +For running all test cases the script requires a RTL simulator to be +found in PATH. Also, the script expects that a test bench is generated +for the processor design by ProGe with \textit{-t} option. + +Below is example usage of test runner from generating a processor +design to running tests on it. + +\begin{verbatim} +> generateprocessor -t -o proge-output -i some.idf some.adf +> generatebits -d -w 4 -x proge-output some.adf +> generatetests -a some.adf -i some.idf -x proge-output +> cd proge-output +> ./testrunner.py + OK ALU-operation-tests + OK icache-test + FAIL mul-operation-tests (bustrace mismatch) +> +\end{verbatim} + + \chapter{CO-DESIGN TOOLS} \label{section:codesign} diff --git a/openasip/hdb/generate_base32.hdb b/openasip/hdb/generate_base32.hdb index bc1fd541d3..48bcbcebfa 100755 Binary files a/openasip/hdb/generate_base32.hdb and b/openasip/hdb/generate_base32.hdb differ diff --git a/openasip/hdb/generate_base32/vhdl/rotl.vhd b/openasip/hdb/generate_base32/vhdl/rotl.vhd index 9a9524d817..3c739205ff 100755 --- a/openasip/hdb/generate_base32/vhdl/rotl.vhd +++ b/openasip/hdb/generate_base32/vhdl/rotl.vhd @@ -1 +1 @@ -op3 <= std_logic_vector(rotate_left(unsigned(op1), to_integer(unsigned(op2)))); +op3 <= std_logic_vector(rotate_left(unsigned(op1), to_integer(signed(op2)))); diff --git a/openasip/hdb/generate_base32/vhdl/rotr.vhd b/openasip/hdb/generate_base32/vhdl/rotr.vhd index 81f16547dc..530469ac7c 100755 --- a/openasip/hdb/generate_base32/vhdl/rotr.vhd +++ b/openasip/hdb/generate_base32/vhdl/rotr.vhd @@ -1 +1 @@ -op3 <= std_logic_vector(rotate_right(unsigned(op1), to_integer(unsigned(op2)))); +op3 <= std_logic_vector(rotate_right(unsigned(op1), to_integer(signed(op2)))); diff --git a/openasip/hdb/generate_lsu/generate.py b/openasip/hdb/generate_lsu/generate.py index cd26f9c8cf..a4748d3a12 100755 --- a/openasip/hdb/generate_lsu/generate.py +++ b/openasip/hdb/generate_lsu/generate.py @@ -78,7 +78,9 @@ def case_start(bits: int, bus_width: int, signal: str, lang: str): low = high - bits + 1 if lang == "vhdl": return "case {} is".format(slice(signal, high, low, lang)) - else: + elif lang == "verilog": + return "case ({})".format(slice(signal, high, low, lang)) + else: # SystemVerilog return "unique case ({})".format(slice(signal, high, low, lang)) def case_end(lang: str): @@ -118,7 +120,7 @@ def extend(width: int, value: str, lang: str): if lang == "vhdl": return "({}-1 downto 0 => {})".format(int(width), value); else: - return "{{{0}{{{1}}}".format(int(width), value) + return "{{{0}{{{1}}}}}".format(int(width), value) def bit_select(signal: str, bit: int, lang: str): @@ -396,7 +398,7 @@ def add_operation(op: Operation, resource_id: int, c): c.execute("INSERT INTO operation_implementation VALUES (NULL, ?, ?, ?, ?, ?, ?, ?)", (latency, op.name, bus_definition, post_op_vhdl, post_op_vlog, - "generate_lsu/shared/defaults.vhdl", "generate_lsu/shared/defaults.vlog")) + "generate_lsu/shared/defaults.vhdl", "generate_lsu/shared/defaults.v")) op_impl_id = c.lastrowid; c.execute("INSERT INTO block_source_file VALUES (NULL, ?, 0)", (main_vhdl,)) @@ -413,7 +415,7 @@ def add_operation(op: Operation, resource_id: int, c): c.execute("INSERT INTO operation_implementation_globalsignal VALUES (NULL, ?, ?, ?, ?, ?, ?)", (op_impl_id, "load_data_32b", "32", "Logic", "VHDL", "0")) c.execute("INSERT INTO operation_implementation_globalsignal VALUES (NULL, ?, ?, ?, ?, ?, ?)", - (op_impl_id, "load_data_32b", "32", "Logic", "VHDL", "0")) + (op_impl_id, "load_data_32b", "32", "Logic", "Verilog", "0")) else: c.execute("INSERT INTO operation_implementation_globalsignal VALUES (NULL, ?, ?, ?, ?, ?, ?)", (op_impl_id, "strobe_32b", "4", "Logic", "VHDL", "0")) @@ -458,6 +460,9 @@ def main(hdb_filename): ("lsu_registers_{}".format(bus_width), "generate_lsu/" + reg_file)) resource_id = c.lastrowid c.execute("INSERT INTO block_source_file VALUES (NULL, \"generate_lsu/shared/lsu_registers.vhdl\", 1)") + c.execute("INSERT INTO operation_implementation_resource_source_file VALUES (NULL, ?, ?)", + (resource_id, c.lastrowid)) + c.execute("INSERT INTO block_source_file VALUES (NULL, \"generate_lsu/shared/lsu_registers.v\", 2)") c.execute("INSERT INTO operation_implementation_resource_source_file VALUES (NULL, ?, ?)", (resource_id, c.lastrowid)) diff --git a/openasip/hdb/generate_lsu/shared/defaults.v b/openasip/hdb/generate_lsu/shared/defaults.v index 392cf8bb82..5e2b1954d5 100755 --- a/openasip/hdb/generate_lsu/shared/defaults.v +++ b/openasip/hdb/generate_lsu/shared/defaults.v @@ -13,3 +13,6 @@ adata_out = adata_out_1; rvalid_in_1 = rvalid_in; rready_out = rready_out_1; +rdata_in_1 = rdata_in; + +glockreq = glockreq_out_1; diff --git a/openasip/hdb/generate_lsu/shared/lsu_registers.v b/openasip/hdb/generate_lsu/shared/lsu_registers.v new file mode 100644 index 0000000000..8a31ef42da --- /dev/null +++ b/openasip/hdb/generate_lsu/shared/lsu_registers.v @@ -0,0 +1,141 @@ +// Copyright (c) 2024 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. +//////////////////////////////////////////////////////////////////////////////- +// Title : LSU interface registers +//////////////////////////////////////////////////////////////////////////////- +// File : lsu_registers.v +// Author : Joonas Multanen +// Company : +// Created : 2024-03-22 +// Last update: 2024-03-22 +// Platform : +//////////////////////////////////////////////////////////////////////////////- +// Description: LSU interface, handling registers and locking +// +// Revisions : +// Date Version Author Description +// 2024-03-22 1.0 multanej Created +//////////////////////////////////////////////////////////////////////////////- + +module lsu_registers #( + parameter dataw_g = 32, + parameter low_bits_g = 2, + parameter addrw_g = 11 +) +( + input wire clk, + input wire rstx, + input wire glock_in, + output reg glockreq_out, + + input wire avalid_in, + input wire awren_in, + input wire [addrw_g-1:0] aaddr_in, + input wire [dataw_g/8-1:0] astrb_in, + input wire [dataw_g-1:0] adata_in, + + output wire avalid_out, + input wire aready_in, + output wire [addrw_g-low_bits_g-1:0] aaddr_out, + output wire awren_out, + output wire [dataw_g/8-1:0] astrb_out, + output wire [dataw_g-1:0] adata_out, + + input wire rvalid_in, + output wire rready_out, + + input wire [dataw_g-1:0] rdata_in, + output reg [dataw_g-1:0] rdata_out, + + output wire [low_bits_g-1:0] addr_low_out +); + +// Access channel registers +reg avalid_r, awren_r, rready_r, rready_rr; +reg [addrw_g-1:0] aaddr_r; +reg [dataw_g/8-1:0] astrb_r; +reg [dataw_g-1:0] adata_r, rdata_r; +reg [low_bits_g-1:0] addr_low_r, addr_low_rr; + +reg fu_glock, glockreq; + +always @(posedge clk or negedge rstx) begin + if (!rstx) begin + avalid_r <= 1'b0; + awren_r <= 1'b0; + aaddr_r <= {addrw_g{1'b0}}; + astrb_r <= {dataw_g/8{1'b0}}; + adata_r <= {dataw_g{1'b0}}; + rready_r <= 1'b0; + rready_rr <= 1'b0; + addr_low_r <= {low_bits_g{1'b0}}; + addr_low_rr <= {low_bits_g{1'b0}}; + end else begin + if (avalid_r && aready_in) + avalid_r <= 1'b0; + if (rready_rr && rvalid_in) begin + rready_rr <= 1'b0; + rdata_r <= rdata_in; + end + if (!fu_glock) begin + avalid_r <= avalid_in; + aaddr_r <= aaddr_in[addrw_g-1:low_bits_g]; + addr_low_r <= aaddr_in[low_bits_g-1:0]; + addr_low_rr <= addr_low_r[low_bits_g-1:0]; + awren_r <= awren_in; + astrb_r <= astrb_in; + adata_r <= adata_in; + if (avalid_in && !awren_in) + rready_r <= 1'b1; + else + rready_r <= 1'b0; + if (rready_r) + rready_rr <= 1'b1; + end + end +end + +always @(*) begin + if (rready_rr && rvalid_in) + rdata_out = rdata_in; + else + rdata_out = rdata_r; +end + +assign avalid_out = avalid_r; +assign awren_out = awren_r; +assign aaddr_out = aaddr_r; +assign astrb_out = astrb_r; +assign adata_out = adata_r; +assign rready_out = rready_rr; +assign addr_low_out = addr_low_rr; + +always @(*) begin + if ((rready_rr && !rvalid_in) || (avalid_r && !aready_in)) + glockreq = 1'b1; + else + glockreq = 1'b0; + fu_glock = glockreq | glock_in; + glockreq_out = glockreq; +end + +endmodule diff --git a/openasip/hdb/generate_lsu/shared/lsu_registers.vhdl b/openasip/hdb/generate_lsu/shared/lsu_registers.vhdl index ee308a0e06..a50ec742f9 100755 --- a/openasip/hdb/generate_lsu/shared/lsu_registers.vhdl +++ b/openasip/hdb/generate_lsu/shared/lsu_registers.vhdl @@ -26,7 +26,7 @@ -- Author : Kati Tervo -- Company : -- Created : 2019-08-27 --- Last update: 2019-08-27 +-- Last update: 2024-03-22 -- Platform : ------------------------------------------------------------------------------- -- Description: LSU interface, handling registers and locking @@ -135,6 +135,7 @@ begin rready_rr <= '0'; addr_low_r <= (others => '0'); addr_low_rr <= (others => '0'); + rdata_r <= (others => '0'); elsif rising_edge(clk) then if avalid_r = '1' and aready_in = '1' then diff --git a/openasip/hdb/generate_rf_iu/verilog/rf_1wr_1rd_lat1.v b/openasip/hdb/generate_rf_iu/verilog/rf_1wr_1rd_lat1.v index a69ccf12d2..cca50ede1f 100755 --- a/openasip/hdb/generate_rf_iu/verilog/rf_1wr_1rd_lat1.v +++ b/openasip/hdb/generate_rf_iu/verilog/rf_1wr_1rd_lat1.v @@ -25,8 +25,8 @@ // Lasse Lehtonen 2017. module rf_1wr_1rd_lat1 - #(parameter data_width_g, - parameter depth_g) + #(parameter data_width_g = 32, + parameter depth_g = 32) ( input clk, input rstx, diff --git a/openasip/hdb/mixed_hdl/verilog/tce_util_pkg.vh b/openasip/hdb/mixed_hdl/verilog/tce_util_pkg.vh index bd2efbda06..6162bd3306 100644 --- a/openasip/hdb/mixed_hdl/verilog/tce_util_pkg.vh +++ b/openasip/hdb/mixed_hdl/verilog/tce_util_pkg.vh @@ -184,9 +184,13 @@ function integer bit_width; input integer value; begin - value=value-1; - for (bit_width=0; value>0; bit_width=bit_width+1) + if (value == 1) begin + bit_width = 1; + end else begin + value=value-1; + for (bit_width=0; value>0; bit_width=bit_width+1) value = value>>1; + end end endfunction @@ -198,4 +202,4 @@ flip_bits[i]=flipped_vec[dataw-i-1]; end endfunction - \ No newline at end of file + diff --git a/openasip/icdecoder_plugins/DefaultDecoderGenerator.cc b/openasip/icdecoder_plugins/DefaultDecoderGenerator.cc index f962c9c61d..7fef3ed6c3 100644 --- a/openasip/icdecoder_plugins/DefaultDecoderGenerator.cc +++ b/openasip/icdecoder_plugins/DefaultDecoderGenerator.cc @@ -877,15 +877,17 @@ DefaultDecoderGenerator::writeInstructionDecoder(std::ostream& stream) { } else { //language_ == Verilog const std::string DS = FileSystem::DIRECTORY_SEPARATOR; string entityName = entityNameStr_ + "_decoder"; - stream << "`timescale 1ns/1ns" << endl - << "module " << entityName << endl + stream << "module " << entityName << endl << "#(" << endl << "`include \"" + << entityNameStr_ << "_imem_mau_pkg.vh\"" << endl + << "," << endl + << "`include \"" << entityNameStr_ << "_globals_pkg.vh\"" << endl << "," << endl << "`include \"" << "gcu_opcodes_pkg.vh\"" << endl << ")" << endl; - + VerilogNetlistWriter::writePortDeclaration( *decoderBlock_, 1, indentation(1), stream); @@ -909,7 +911,11 @@ DefaultDecoderGenerator::writeInstructionDecoder(std::ostream& stream) { stream << endl; writeRFCntrlSignals(stream); stream << endl; - + writeGlockHandlingSignals(stream); + stream << endl; + writePipelineFillSignals(stream); + stream << endl; + if (generateLockTrace_) { writeLockDumpCode(stream); stream << endl; @@ -927,7 +933,10 @@ DefaultDecoderGenerator::writeInstructionDecoder(std::ostream& stream) { stream << endl; writeMainDecodingProcess(stream); stream << endl; - + writeGlockMapping(stream); + stream << endl; + writePipelineFillProcess(stream); + int lockReqWidth = glockRequestWidth(); stream << indentation(1) << "assign " << NetlistGenerator::DECODER_LOCK_REQ_OUT_PORT << "="; @@ -946,7 +955,7 @@ DefaultDecoderGenerator::writeInstructionDecoder(std::ostream& stream) { const int glockWidth = glockPortWidth(); stream << indentation(1) << "assign " << GLOCK_PORT_NAME << " = {" << Conversion::toString(glockWidth) << "{" - << NetlistGenerator::DECODER_LOCK_REQ_IN_PORT << "}};" << endl + << POST_DECODE_MERGED_GLOCK_SIGNAL << "}};" << endl << endl; stream << endl << "endmodule" << endl; } @@ -1315,7 +1324,7 @@ DefaultDecoderGenerator::writeRFCntrlSignals(std::ostream& stream) { for (int i = 0; i < rf->portCount(); i++) { RFPort* port = rf->port(i); bool async_signal = sacEnabled(rf->name()) - && port->outputSocket() != NULL; + && port->outputSocket() != NULL; // load signal std::string sigName = @@ -1385,7 +1394,6 @@ DefaultDecoderGenerator::writeRFCntrlSignals(std::ostream& stream) { void DefaultDecoderGenerator::writeGlockHandlingSignals( std::ostream& stream) const { - assert(language_ == VHDL && "Support for other HDL not yet implemented."); writeSignalDeclaration( stream, ProGe::BIT, INTERNAL_MERGED_GLOCK_REQ_SIGNAL, 1); writeSignalDeclaration( @@ -1627,10 +1635,10 @@ DefaultDecoderGenerator::writeSquashSignalGenerationProcess( stream << "conv_integer(unsigned(" << LIMM_TAG_SIGNAL << ")) = " << icField.templateEncoding(affectingTemp->name()); - stream << ") then" << endl; - stream << indentation(indLevel+1) << squashSignal(bus.name()) - << " <= '1';" << endl; } + stream << ") then" << endl; + stream << indentation(indLevel+1) << squashSignal(bus.name()) + << " <= '1';" << endl; } if (ifClauseStarted) { @@ -2402,111 +2410,205 @@ DefaultDecoderGenerator::writeMainDecodingProcess( */ void DefaultDecoderGenerator::writeGlockMapping(std::ostream& stream) const { - assert( - language_ == VHDL && - "writeGlockMapping() is not yet implemented " - "for other HDLs."); - - // Generate output register for core lock status signal // - string propagateGlock( - POST_DECODE_MERGED_GLOCK_OUTREG + - " <= " + POST_DECODE_MERGED_GLOCK_SIGNAL + ";"); - string assertGlock(POST_DECODE_MERGED_GLOCK_OUTREG + " <= '1'" + ";"); - if (syncReset_) { - stream << " lock_reg_proc : process (clk)\n" - << " begin\n" - << " if (clk'event and clk = '1') then\n" - << " if (rstx = '0') then\n" - << " -- Locked during active reset\n" - << " " << assertGlock << "\n" - << " else\n" - << " " << propagateGlock << "\n" - << " end if;\n" - << " end if;\n" - << " end process lock_reg_proc;\n\n"; - } else { - stream << " lock_reg_proc : process (clk, rstx)\n" - << " begin\n" - << " if (rstx = '0') then\n" - << " -- Locked during active reset\n" - << " " << assertGlock << "\n" - << " elsif (clk'event and clk = '1') then\n" - << " " << propagateGlock << "\n" - << " end if;\n" - << " end process lock_reg_proc;\n\n"; - } + if (language_ == VHDL) { + // Generate output register for core lock status signal // + string propagateGlock( + POST_DECODE_MERGED_GLOCK_OUTREG + + " <= " + POST_DECODE_MERGED_GLOCK_SIGNAL + ";"); + string assertGlock(POST_DECODE_MERGED_GLOCK_OUTREG + " <= '1'" + ";"); + if (syncReset_) { + stream << " lock_reg_proc : process (clk)\n" + << " begin\n" + << " if (clk'event and clk = '1') then\n" + << " if (rstx = '0') then\n" + << " -- Locked during active reset\n" + << " " << assertGlock << "\n" + << " else\n" + << " " << propagateGlock << "\n" + << " end if;\n" + << " end if;\n" + << " end process lock_reg_proc;\n\n"; + } else { + stream << " lock_reg_proc : process (clk, rstx)\n" + << " begin\n" + << " if (rstx = '0') then\n" + << " -- Locked during active reset\n" + << " " << assertGlock << "\n" + << " elsif (clk'event and clk = '1') then\n" + << " " << propagateGlock << "\n" + << " end if;\n" + << " end process lock_reg_proc;\n\n"; + } + + // Generate global lock request wiring // + int lockReqWidth = glockRequestWidth(); + stream << indentation(1) << NetlistGenerator::DECODER_LOCK_REQ_OUT_PORT + << " <= " << INTERNAL_MERGED_GLOCK_REQ_SIGNAL << ";" << endl; + stream << indentation(1) << INTERNAL_MERGED_GLOCK_REQ_SIGNAL << " <= "; + if (lockReqWidth > 0) { + for (int i = 0; i < lockReqWidth; i++) { + stream << LOCK_REQ_PORT_NAME << "(" << i << ")"; + if (i + 1 < lockReqWidth) { + stream << " or "; + } + } + stream << ";" << endl; + } else { + stream << "'0';" << endl; + } - // Generate global lock request wiring // - int lockReqWidth = glockRequestWidth(); - stream << indentation(1) << NetlistGenerator::DECODER_LOCK_REQ_OUT_PORT - << " <= " << INTERNAL_MERGED_GLOCK_REQ_SIGNAL << ";" << endl; - stream << indentation(1) << INTERNAL_MERGED_GLOCK_REQ_SIGNAL << " <= "; - if (lockReqWidth > 0) { - for (int i = 0; i < lockReqWidth; i++) { - stream << LOCK_REQ_PORT_NAME << "(" << i << ")"; - if (i + 1 < lockReqWidth) { - stream << " or "; + stream << indentation(1) << PRE_DECODE_MERGED_GLOCK_SIGNAL + << " <= " << NetlistGenerator::DECODER_LOCK_REQ_IN_PORT; + if (lockReqWidth > 0) { + stream << " or " << INTERNAL_MERGED_GLOCK_REQ_SIGNAL << ";" << endl; + } else { + stream << ";" << endl; + } + stream << indentation(1) << POST_DECODE_MERGED_GLOCK_SIGNAL + << " <= " << PRE_DECODE_MERGED_GLOCK_SIGNAL << " or " + << PIPELINE_FILL_LOCK_SIGNAL << ";" << endl; + stream << indentation(1) << NetlistGenerator::DECODER_LOCK_STATUS_PORT + << " <= " << POST_DECODE_MERGED_GLOCK_OUTREG << ";" << endl; + + // Generate global lock wiring // + const int glockWidth = glockPortWidth(); + for (GlockBitType glockBitToConnect = 0; glockBitToConnect < glockWidth; + glockBitToConnect++) { + stream << indentation(1) << GLOCK_PORT_NAME << "(" + << Conversion::toString(glockBitToConnect) << ") <= "; + + // If the feature for alternate glock wiring is enabled and current + // glock signal to be wired for the TTA Unit has glock request port. + if (generateAlternateGlockReqHandling_ && + MapTools::containsKey(unitGlockBitMap_, glockBitToConnect) && + MapTools::containsKey( + unitGlockReqBitMap_, + unitGlockBitMap_.find(glockBitToConnect)->second)) { + // Specialized global lock port map to avoid self-locking of FU. + // Each FU that has global lock request will not receive + // global lock signal unless another FU request global lock. + const Unit* associatedToGlockReq = + unitGlockBitMap_.find(glockBitToConnect)->second; + UnitGlockReqBitMapType::const_iterator gr_it; + for (gr_it = unitGlockReqBitMap_.begin(); + gr_it != unitGlockReqBitMap_.end(); gr_it++) { + if (gr_it->first == associatedToGlockReq) { + continue; + } + GlockReqBitType glockReqBitToConnect = gr_it->second; + stream << LOCK_REQ_PORT_NAME << "(" + << Conversion::toString(glockReqBitToConnect) + << ") or "; + } + stream << NetlistGenerator::DECODER_LOCK_REQ_IN_PORT << ";"; + } else { + // Regular global lock port map. + stream << POST_DECODE_MERGED_GLOCK_SIGNAL << ";"; + } + if (MapTools::containsKey(unitGlockBitMap_, glockBitToConnect)) { + stream + << " -- to " + << unitGlockBitMap_.find(glockBitToConnect)->second->name(); } + stream << endl; } - stream << ";" << endl; - } else { - stream << "'0';" << endl; - } + } else { // Verilog + string propagateGlock( + POST_DECODE_MERGED_GLOCK_OUTREG + + " = " + POST_DECODE_MERGED_GLOCK_SIGNAL + ";"); + string assertGlock(POST_DECODE_MERGED_GLOCK_OUTREG + " = '1" + ";"); - stream << indentation(1) << PRE_DECODE_MERGED_GLOCK_SIGNAL - << " <= " << NetlistGenerator::DECODER_LOCK_REQ_IN_PORT; - if (lockReqWidth > 0) { - stream << " or " << INTERNAL_MERGED_GLOCK_REQ_SIGNAL << ";" << endl; - } else { - stream << ";" << endl; - } - stream << indentation(1) << POST_DECODE_MERGED_GLOCK_SIGNAL - << " <= " << PRE_DECODE_MERGED_GLOCK_SIGNAL << " or " - << PIPELINE_FILL_LOCK_SIGNAL << ";" << endl; - stream << indentation(1) << NetlistGenerator::DECODER_LOCK_STATUS_PORT - << " <= " << POST_DECODE_MERGED_GLOCK_OUTREG << ";" << endl; - - // Generate global lock wiring // - const int glockWidth = glockPortWidth(); - for (GlockBitType glockBitToConnect = 0; glockBitToConnect < glockWidth; - glockBitToConnect++) { - stream << indentation(1) << GLOCK_PORT_NAME << "(" - << Conversion::toString(glockBitToConnect) << ") <= "; - - // If the feature for alternate glock wiring is enabled and current - // glock signal to be wired for the TTA Unit has glock request port. - if (generateAlternateGlockReqHandling_ && - MapTools::containsKey(unitGlockBitMap_, glockBitToConnect) && - MapTools::containsKey( - unitGlockReqBitMap_, - unitGlockBitMap_.find(glockBitToConnect)->second)) { - // Specialized global lock port map to avoid self-locking of FU. - // Each FU that has global lock request will not receive - // global lock signal unless another FU request global lock. - const Unit* associatedToGlockReq = - unitGlockBitMap_.find(glockBitToConnect)->second; - UnitGlockReqBitMapType::const_iterator gr_it; - for (gr_it = unitGlockReqBitMap_.begin(); - gr_it != unitGlockReqBitMap_.end(); gr_it++) { - if (gr_it->first == associatedToGlockReq) { - continue; + if (syncReset_) { + assert(false && "synch reset not yet implemented in Verilog."); + } else { + stream << " always@(posedge clk or negedge rstx) begin" << endl + << " if (~rstx) begin" << endl + << " // Locked during active reset" << endl + << " " << assertGlock << endl + << " end else begin" << endl + << " " << propagateGlock << endl + << " end" << endl + << " end" << endl; + } + + // Generate global lock request wiring // + int lockReqWidth = glockRequestWidth(); + stream << indentation(1) << "assign " + << NetlistGenerator::DECODER_LOCK_REQ_OUT_PORT + << " = " << INTERNAL_MERGED_GLOCK_REQ_SIGNAL << ";" << endl; + stream << indentation(1) << "assign " + << INTERNAL_MERGED_GLOCK_REQ_SIGNAL << " = "; + if (lockReqWidth > 0) { + for (int i = 0; i < lockReqWidth; i++) { + stream << LOCK_REQ_PORT_NAME << "[" << i << "]"; + if (i + 1 < lockReqWidth) { + stream << " | "; } - GlockReqBitType glockReqBitToConnect = gr_it->second; - stream << LOCK_REQ_PORT_NAME << "(" - << Conversion::toString(glockReqBitToConnect) - << ") or "; } - stream << NetlistGenerator::DECODER_LOCK_REQ_IN_PORT << ";"; + stream << ";" << endl; + } else { + stream << "'0;" << endl; + } + + stream << indentation(1) << "assign " + << PRE_DECODE_MERGED_GLOCK_SIGNAL + << " = " << NetlistGenerator::DECODER_LOCK_REQ_IN_PORT; + if (lockReqWidth > 0) { + stream << " | " << INTERNAL_MERGED_GLOCK_REQ_SIGNAL << ";" << endl; } else { - // Regular global lock port map. - stream << POST_DECODE_MERGED_GLOCK_SIGNAL << ";"; + stream << ";" << endl; } - if (MapTools::containsKey(unitGlockBitMap_, glockBitToConnect)) { - stream - << " -- to " - << unitGlockBitMap_.find(glockBitToConnect)->second->name(); + stream << indentation(1) << "assign " + << POST_DECODE_MERGED_GLOCK_SIGNAL + << " = " << PRE_DECODE_MERGED_GLOCK_SIGNAL << " | " + << PIPELINE_FILL_LOCK_SIGNAL << ";" << endl; + stream << indentation(1) << "assign " + << NetlistGenerator::DECODER_LOCK_STATUS_PORT + << " = " << POST_DECODE_MERGED_GLOCK_OUTREG << ";" << endl; + + // Generate global lock wiring // + const int glockWidth = glockPortWidth(); + for (GlockBitType glockBitToConnect = 0; glockBitToConnect < glockWidth; + glockBitToConnect++) { + stream << indentation(1) << "assign " + << GLOCK_PORT_NAME << "[" + << Conversion::toString(glockBitToConnect) << "] = "; + + // If the feature for alternate glock wiring is enabled and current + // glock signal to be wired for the TTA Unit has glock request port. + if (generateAlternateGlockReqHandling_ && + MapTools::containsKey(unitGlockBitMap_, glockBitToConnect) && + MapTools::containsKey( + unitGlockReqBitMap_, + unitGlockBitMap_.find(glockBitToConnect)->second)) { + // Specialized global lock port map to avoid self-locking of FU. + // Each FU that has global lock request will not receive + // global lock signal unless another FU request global lock. + const Unit* associatedToGlockReq = + unitGlockBitMap_.find(glockBitToConnect)->second; + UnitGlockReqBitMapType::const_iterator gr_it; + for (gr_it = unitGlockReqBitMap_.begin(); + gr_it != unitGlockReqBitMap_.end(); gr_it++) { + if (gr_it->first == associatedToGlockReq) { + continue; + } + GlockReqBitType glockReqBitToConnect = gr_it->second; + stream << LOCK_REQ_PORT_NAME << "[" + << Conversion::toString(glockReqBitToConnect) + << "] | "; + } + stream << NetlistGenerator::DECODER_LOCK_REQ_IN_PORT << ";"; + } else { + // Regular global lock port map. + stream << POST_DECODE_MERGED_GLOCK_SIGNAL << ";"; + } + if (MapTools::containsKey(unitGlockBitMap_, glockBitToConnect)) { + stream + << " // to " + << unitGlockBitMap_.find(glockBitToConnect)->second->name(); + } + stream << endl; } - stream << endl; } } @@ -2545,7 +2647,19 @@ DefaultDecoderGenerator::writePipelineFillProcess( indstream(1) << "end process decode_pipeline_fill_lock;" << endl; } else { // language_ == Verilog - // todo + if (syncReset_) { + assert(false && "synch reset not yet implemented in Verilog."); + } else { + indstream(1) << "always@(posedge clk or negedge rstx) begin" << endl; + indstream(2) << "if (~rstx) begin" << endl; + indstream(3) << "decode_fill_lock_reg <= '1;" << endl; + indstream(2) << "end else begin" << endl; + indstream(3) << "if (lock == '0) begin" << endl; + indstream(4) << "decode_fill_lock_reg <= '0;" << endl; + indstream(3) << "end" << endl; + indstream(2) << "end" << endl; + indstream(1) << "end" << endl; + } } } @@ -4953,6 +5067,11 @@ bool DefaultDecoderGenerator::sacEnabled(const string& rfName) const { assert(nlGenerator_ != NULL); - const RFEntry& rfEntry = nlGenerator_->rfEntry(rfName); - return rfEntry.implementation().separateAddressCycleParameter(); + // RFGenerated RFs do not have a HDB entry. + if (nlGenerator_->rfHasEntry(rfName)) { + const RFEntry& rfEntry = nlGenerator_->rfEntry(rfName); + return rfEntry.implementation().separateAddressCycleParameter(); + } else { + return false; + } } diff --git a/openasip/icdecoder_plugins/DefaultICGenerator.cc b/openasip/icdecoder_plugins/DefaultICGenerator.cc index 17ef92d7f2..63273a3085 100644 --- a/openasip/icdecoder_plugins/DefaultICGenerator.cc +++ b/openasip/icdecoder_plugins/DefaultICGenerator.cc @@ -560,8 +560,7 @@ DefaultICGenerator::generateInputMux( stream << "end rtl;" << endl; } else { string entityName = inputMuxEntityName(segmentConns); - stream << "`timescale 10ns/1ns" << endl - << "module " << entityName << "" << endl << endl; + stream << "module " << entityName << "" << endl << endl; writeInputSocketComponentDeclaration(Verilog,segmentConns, 1, stream); stream << indentation(2) << "// If width of input bus is greater than width of output," @@ -765,8 +764,7 @@ DefaultICGenerator::generateOutputSocket( stream << indentation(1) << "end process output;" << endl << endl; stream << "end output_socket_andor;" << endl; } else { // language_ == Verilog - stream << "`timescale 10ns/1ns" << endl - << "module " << entityName << endl; + stream << "module " << entityName << endl; writeOutputSocketComponentDeclaration(Verilog, portConns, segmentConns, 1, stream); @@ -1174,11 +1172,13 @@ DefaultICGenerator::writeInterconnectionNetwork(std::ostream& stream) { } else { // language_ == Verilog const std::string DS = FileSystem::DIRECTORY_SEPARATOR; string entityName = entityNameStr_ + "_interconn"; - stream << "`timescale 1ns/1ns" << endl - << "module " << entityName << endl + stream << "module " << entityName << endl //include parameters here << "#(" << endl << "`include \"" + << entityNameStr_ << "_imem_mau_pkg.vh\"" << endl + << "," << endl + << "`include \"" << entityNameStr_ << "_globals_pkg.vh\"" << endl << ")" << endl; @@ -1838,7 +1838,7 @@ DefaultICGenerator::writeBusDumpCode(std::ostream& stream) const { << indentation(1) << "//synthesis translate_off" << endl << indentation(1) << "integer regularfileout;" << endl << endl << indentation(1) << "integer executionfileout;" << endl << endl - << indentation(1) << "integer count=0;" << endl + << indentation(1) << "integer count=-1;" << endl << indentation(1) << "integer executioncount=0;" << endl << endl << indentation(1) << "`define REGULARDUMPFILE \"bus.dump\"" << indentation(1) @@ -1856,7 +1856,7 @@ DefaultICGenerator::writeBusDumpCode(std::ostream& stream) const { << indentation(2) << "$fclose(executionfileout);" << endl << indentation(2) << "forever" << endl << indentation(2) << "begin" << endl - << indentation(3) << "#PERIOD;" << endl; + << indentation(3) << "@(posedge clk);" << endl; if (busTraceStartingCycle_ > 0) { stream << indentation(3) << "if(count > " << busTraceStartingCycle_ - 1 diff --git a/openasip/src/applibs/HWGen/FUGen.cc b/openasip/src/applibs/HWGen/FUGen.cc index 9c57ff2bfb..1920f06261 100644 --- a/openasip/src/applibs/HWGen/FUGen.cc +++ b/openasip/src/applibs/HWGen/FUGen.cc @@ -674,17 +674,17 @@ FUGen::buildOperations() { CodeBlock defaultValues; CodeBlock defaultSnippets; CodeBlock triggeredSnippets; + CodeBlock operationOutAssignments; for (auto&& d : extOutputs_) { operationCp << DefaultAssign(d.first, d.second); } for (std::string signal : resourceInputs_) { - // Zero initialize this configuration to avoid simulation warnings - if (isLSU_ && minLatency_ < 3) { - operationCp << DefaultAssign(signal, "0"); - } else { + if (options_.dontCareInitialization) { operationCp << DefaultAssign(signal, "-"); + } else { + operationCp << DefaultAssign(signal, "0"); } } @@ -710,34 +710,32 @@ FUGen::buildOperations() { for (int id : schedule.results) { std::string source = operandSignal(name, id); - // Zero initialize this configuration to avoid simulation warnings - if (isLSU_ && minLatency_ < 3) { - defaultValues.append(DefaultAssign(source, "0")); - } else { - defaultValues.append(DefaultAssign(source, "-")); - } + // Zero initialize always + defaultValues.append(DefaultAssign(source, "0")); } for (auto&& operand : schedule.operands) { int id = operand.id; - std::string source = operand.signalName; std::string destination = operandSignal(name, id); + CodeBlock* cb = &defaultValues; + if (operand.isOutput) { + cb = &operationOutAssignments; + } if (operand.portWidth > operand.operandWidth) { - defaultValues.append(Assign( + cb->append(Assign( destination, Splice(source, operand.operandWidth - 1, 0))); } else if (operand.portWidth < operand.operandWidth) { - defaultValues.append(Assign( + cb->append(Assign( destination, Ext(source, operand.operandWidth, operand.portWidth))); } else { - defaultValues.append(Assign(destination, LHSSignal(source))); + cb->append(Assign(destination, LHSSignal(source))); } - if (!operand.isOutput) { operationCp.reads(destination); - } + } } replacesPerOp_[name] = buildReplaces(name); @@ -764,11 +762,31 @@ FUGen::buildOperations() { for (std::string subop : schedules) { auto schedule = scheduledOperations_[subop]; std::string baseOp = schedule.baseOp; + if (schedule.initialCycle == cycle) { auto& impl = baseOperations_[baseOp].implementation; if (!impl.empty()) { std::set statements; prepareSnippet(subop, impl, onTrigger, statements); + + for (auto&& operand : schedule.operands) { + int id = operand.id; + std::string srcSignal = operand.signalName; + std::string dstSignal = operandSignal(subop, id); + if (TCEString(dstSignal).startsWith("subop") && !operand.isOutput) { + if (operand.portWidth > operand.operandWidth) { + onTrigger.append(Assign( + dstSignal, + Splice(srcSignal, operand.operandWidth - 1, 0))); + } else if (operand.portWidth < operand.operandWidth) { + onTrigger.append(Assign( + dstSignal, + Ext(srcSignal, operand.operandWidth, operand.portWidth))); + } else { + onTrigger.append(Assign(dstSignal, LHSSignal(srcSignal))); + } + } + } } emptyBlock = false; } @@ -827,26 +845,25 @@ FUGen::buildOperations() { } else { operationCp.addVariable(SignedVariable(v.name, w)); } - // Zero initialize this configuration to avoid simulation warnings - if (isLSU_ && minLatency_ < 3) { - operationCp << DefaultAssign(v.name, "0"); - } else { + if (options_.dontCareInitialization) { operationCp << DefaultAssign(v.name, "-"); + } else { + operationCp << DefaultAssign(v.name, "0"); } } for (auto&& s : renamedGlobalSignals_) { int w = std::stoi(s.width); fu_ << Wire(s.name, w); // creates the signal declaration operationCp.reads(s.name); // adds it to sensitivity list - // Zero initialize this configuration to avoid simulation warnings - if (isLSU_ && minLatency_ < 3) { - operationCp << DefaultAssign(s.name, "0"); - } else { + if (options_.dontCareInitialization) { operationCp << DefaultAssign(s.name, "-"); + } else { + operationCp << DefaultAssign(s.name, "0"); } } - operationCp << defaultValues << defaultSnippets << triggeredSnippets; + operationCp << defaultValues << defaultSnippets + << triggeredSnippets << operationOutAssignments; behaviour_ << operationCp; } @@ -892,7 +909,7 @@ FUGen::finalizeHDL() { fu_ << Wire("glockreq"); behaviour_ << Assign("glockreq_out", LHSSignal("glockreq")); } else { - behaviour_ << Assign("glockreq_out", BinaryLiteral("0")); + behaviour_ << Assign("glockreq_out", BinaryLiteral("0"), true); } // Finalize and set global options. diff --git a/openasip/src/applibs/HWGen/HDLGenerator.hh b/openasip/src/applibs/HWGen/HDLGenerator.hh index 1b82acfef1..3cf3eaf90a 100755 --- a/openasip/src/applibs/HWGen/HDLGenerator.hh +++ b/openasip/src/applibs/HWGen/HDLGenerator.hh @@ -322,9 +322,9 @@ namespace HDLGenerator { Width width() final { return {strWidth_, width_}; } - void declare(std::ostream& stream, Language lang, int ident) { + void declare(std::ostream& stream, Language lang, int indent) { if (lang == Language::VHDL) { - stream << StringTools::indent(ident) << "signal " + stream << StringTools::indent(indent) << "signal " << name() << " : "; if (width_ < 0 || width_ > 1 || wt_ == WireType::Vector) { if (strWidth_.empty()) { @@ -339,7 +339,7 @@ namespace HDLGenerator { stream << "std_logic;\n"; } } else if (lang == Language::Verilog) { - stream << StringTools::indent(ident) << "reg "; + stream << StringTools::indent(indent) << "reg "; if (width_ < 0 || width_ > 1) { if (strWidth_.empty()) { stream << "[" << std::to_string(width_ - 1) << ":0] "; @@ -539,15 +539,15 @@ namespace HDLGenerator { */ class Assign : public SequentialStatement { public: - Assign(std::string var, LHSValue value) + Assign(std::string var, LHSValue value, bool isConstant = false) : SequentialStatement(var), index_(-1), upperBound_(-1), - lowerBound_(-1), value_(value) {} - Assign(std::string var, LHSValue value, int idx) + 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) {} - Assign(std::string var, LHSValue value, int ub, int lb) + 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) {} + lowerBound_(lb), value_(value), isConstant_(isConstant) {} void build() override { Generatable::build(); @@ -579,7 +579,9 @@ namespace HDLGenerator { stream << " <= "; } } else if (lang == Language::Verilog) { - if (!(parentIs() || parentIs())) { + if (isConstant_) { + stream << StringTools::indent(level) << "assign " << name(); + } else if (!(parentIs() || parentIs() )) { stream << StringTools::indent(level) << "always @*\n" << StringTools::indent(level + 1) << name(); } else { @@ -607,6 +609,7 @@ namespace HDLGenerator { int upperBound_; int lowerBound_; LHSValue value_; + bool isConstant_; }; /** @@ -839,7 +842,7 @@ namespace HDLGenerator { stream << StringTools::indent(level) << "end else if "; } iter->first.hdl(stream, lang); - stream << ") begin\n"; + stream << " begin\n"; iter->second->hdl(stream, lang, level + 1); } if (elseBlock_ != nullptr) { @@ -1138,6 +1141,11 @@ namespace HDLGenerator { return *this; } + Behaviour& operator<<(RawCodeLine&& rhs) { + addComponent(rhs); + return *this; + } + Behaviour& operator<<(Assign& assignment) { addComponent(assignment); return *this; @@ -1208,6 +1216,11 @@ namespace HDLGenerator { prefix_ = prefix; } + Module& operator<<(RawCodeLine&& rawCodeLine) { + rawCodeLines_.emplace_back(rawCodeLine); + return *this; + } + Module& operator<<(Behaviour& rhs) { behaviours_.emplace_back(new Behaviour(rhs)); return *this; @@ -1468,7 +1481,7 @@ namespace HDLGenerator { p.name() == "glock_in") { stream << p.name() << ")"; } else { - stream << instance << "_" << name() << ")"; + stream << instance << "_" << p.name() << ")"; } separator = ",\n"; } @@ -1482,20 +1495,23 @@ namespace HDLGenerator { clear(); build(); if (lang == Language::VHDL) { - std::string ident = StringTools::indent(level); + std::string indent = StringTools::indent(level); // Header comment for (auto&& line : headerComment_) { - stream << ident << "-- " << line << "\n"; + stream << indent << "-- " << line << "\n"; } // Libraries - stream << ident << "\n" - << ident << "library ieee;\n" - << ident << "use ieee.std_logic_1164.all;\n" - << ident << "use ieee.numeric_std.all;\n" - << ident << "use ieee.std_logic_misc.all;\n" + stream << indent << "\n" + << indent << "library ieee;\n" + << indent << "use ieee.std_logic_1164.all;\n" + << indent << "use ieee.numeric_std.all;\n" + << 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 - << ident << "\n" - << ident << "entity " << name() << " is\n"; + << indent << "\n" + << indent << "entity " << name() << " is\n"; // - Generics if (!parameters_.empty()) { std::string separator = ""; @@ -1518,10 +1534,10 @@ namespace HDLGenerator { } stream << ");\n"; } - stream << ident << "end entity " << name() << ";\n" + stream << indent << "end entity " << name() << ";\n" // Architecture - << ident << "\n" - << ident << "architecture rtl of " + << indent << "\n" + << indent << "architecture rtl of " << name() << " is\n"; // constants if (!constants_.empty() || !binaryConstants_.empty()) { @@ -1547,6 +1563,12 @@ namespace HDLGenerator { for (auto&& r : registers_) { r.declare(stream, lang, level + 1); } + + // Raw code lines + for (auto&& r : rawCodeLines_) { + r.hdl(stream, lang, level + 1); + } + // declare components std::vector declared; for (auto&& m : modules_) { @@ -1638,6 +1660,13 @@ namespace HDLGenerator { for (auto&& r : registers_) { r.declare(stream, lang, level + 1); } + + // Raw code lines + stream << "\n"; + for (auto&& r : rawCodeLines_) { + r.hdl(stream, lang, level + 1); + } + // instantiate stuff for (auto&& m : modules_) { stream << "\n"; @@ -1683,6 +1712,7 @@ namespace HDLGenerator { } int id_ = 0; std::string prefix_; + std::vector rawCodeLines_; std::unordered_set options_; std::vector headerComment_; std::vector parameters_; diff --git a/openasip/src/applibs/HWGen/Makefile.am b/openasip/src/applibs/HWGen/Makefile.am index 8d4a6ca84c..467e0f71fa 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 HDLGenerator.cc LHSValue.cc +libhwgen_la_SOURCES = FUGen.cc RFGen.cc HDLGenerator.cc LHSValue.cc SRC_ROOT_DIR = $(top_srcdir)/src @@ -39,6 +39,7 @@ MAINTAINERCLEANFILES = *~ *.gcov *.bbg *.bb *.da ## headers start libhwgen_la_SOURCES += \ - FUGen.hh HDLGenerator.hh HWGenTools.hh HDLRegister.hh HDLPort.hh \ - LHSValue.hh Generatable.hh WidthTransformations.hh BinaryOps.hh + FUGen.hh RFGen.hh HDLGenerator.hh HWGenTools.hh HDLRegister.hh \ + HDLPort.hh LHSValue.hh Generatable.hh WidthTransformations.hh \ + BinaryOps.hh ## headers end diff --git a/openasip/src/applibs/HWGen/RFGen.cc b/openasip/src/applibs/HWGen/RFGen.cc new file mode 100644 index 0000000000..e05d36faee --- /dev/null +++ b/openasip/src/applibs/HWGen/RFGen.cc @@ -0,0 +1,513 @@ +/* + Copyright (c) 2017-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. + */ +/** + * @file RFGen.cc + * + * Register file generator. + * + * @author Joonas Multanen 2024 (joonas.multanen-no-spam-tuni.fi) + * @note rating: red +*/ + +#include "RFGen.hh" +#include +#include "ProGeTools.hh" +#include "FileSystem.hh" + +using namespace HDLGenerator; + + +std::deque +RFGen::readFile(std::string filename) { + std::deque file; + // replace temps in operation implmentations and keep track of + // read temps. + if (filename == "") { + return file; + } + + std::ifstream implStream(filename); + for (std::string line; std::getline(implStream, line);) { + file.emplace_back(StringTools::stringToLower(line)); + } + return file; +} + +/** + * Searches for file in TCE folders and makes the path absolute. + */ +std::string +RFGen::findAbsolutePath(std::string file) { + if (file.length() > 4 && file.substr(0, 4) == "tce:") { + file = file.substr(4); + } + std::vector paths = Environment::hdbPaths(); + for (auto file : options_.hdbList) { + paths.emplace_back(FileSystem::directoryOfPath(file)); + } + return FileSystem::findFileInSearchPaths(paths, file); +} + +/** + * Creates the header comment for RF. + */ +void +RFGen::createRFHeaderComment() { + rf_.appendToHeader("Register file: " + rfg_.name()); + rf_.appendToHeader(""); + rf_.appendToHeader("Max. number of parallel reads: "); + rf_.appendToHeader(std::to_string(adfRF_->maxReads())); + rf_.appendToHeader("Max. number of parallel writes: "); + rf_.appendToHeader(std::to_string(adfRF_->maxWrites())); + rf_.appendToHeader(""); +} + +/* + * Create actual HDL files. + */ +void +RFGen::createImplementationFiles() { + if (options_.language == ProGe::HDL::VHDL) { + Path dir = Path(options_.outputDirectory) / "vhdl"; + FileSystem::createDirectory(dir.string()); + Path file = dir / (rf_.name() + ".vhd"); + std::ofstream ofs(file); + rf_.implement(ofs, Language::VHDL); + } else if (options_.language == ProGe::HDL::Verilog) { + Path dir = Path(options_.outputDirectory) / "verilog"; + FileSystem::createDirectory(dir.string()); + Path file = dir / (rf_.name() + ".v"); + std::ofstream ofs(file); + rf_.implement(ofs, Language::Verilog); + } + +} + +/* + * Create ports that are always there. + */ +void +RFGen::createMandatoryPorts() { + std::string resetPort; + if (ProGeTools::findInOptionList("active low reset", globalOptions_)) { + resetPort = "rstx"; + } else { + resetPort = "rst"; + } + + rf_ << InPort("clk") << InPort(resetPort) << InPort("glock_in"); + + // operand ports. + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = + static_cast(adfRF_->port(i)); + int opcodeWidth = + static_cast(std::ceil( + std::log2(adfRF_->numberOfRegisters()))); + if (adfPort->isInput()) { + rf_ << InPort( + "data_" + adfPort->name() + "_in", adfPort->width(), + WireType::Vector); + rf_ << InPort("load_" + adfPort->name() + "_in"); + rf_ << InPort("opcode_" + adfPort->name() + "_in", opcodeWidth, + WireType::Vector); + } else { + rf_ << OutPort( + "data_" + adfPort->name() + "_out", adfPort->width(), + WireType::Vector); + rf_ << InPort("load_" + adfPort->name() + "_in"); + rf_ << InPort("opcode_" + adfPort->name() + "_in", opcodeWidth, + WireType::Vector); + } + } + +} + +/* + * Create guard port. + */ +void +RFGen::createGuardPort() { + if (!adfRF_->isUsedAsGuard()) { + return; + } + rf_ << OutPort(guardPortName_, adfRF_->size(), WireType::Vector); +} + +/* + * Create guard process. + */ +void +RFGen::createGuardProcess() { + if (!adfRF_->isUsedAsGuard()) { + return; + } + + std::string vhdlString = std::string("guard_out_cp : PROCESS (") + + mainRegName_; + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = + static_cast(adfRF_->port(i)); + if (adfPort->isInput()) { + std::string loadPortName = "load_" + adfPort->name() + "_in"; + std::string opcodePortName = "opcode_" + adfPort->name() + "_in"; + std::string dataPortName = "data_" + adfPort->name() + "_in"; + vhdlString += ", " + loadPortName + ", " + + opcodePortName + ", " + dataPortName; + } + } + vhdlString += std::string(")\n") + + "BEGIN\n" + +" for i in " + std::to_string(adfRF_->size()) + "-1 downto 0 loop\n"; + + int numInputPorts = 0; + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = + static_cast(adfRF_->port(i)); + if (adfPort->isInput()) { + numInputPorts += 1; + } + } + + int inputPortIndex = 0; + Asynchronous guardPortProcess(guardPortName_ + "_cp"); + if (adfRF_->guardLatency() == 0) { + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = + static_cast(adfRF_->port(i)); + if (!adfPort->isInput()) { + continue; + } + std::string loadPortName = "load_" + adfPort->name() + "_in"; + std::string opcodePortName = "opcode_" + adfPort->name() + "_in"; + std::string dataPortName = "data_" + adfPort->name() + "_in"; + if (inputPortIndex == 0) { + vhdlString += " if "; + } else { + vhdlString += " elsif "; + } + vhdlString += + loadPortName + " = '1' and i = to_integer(unsigned(" + + opcodePortName + ")) then\n"; + vhdlString += + " " + guardPortName_ + "(i) <= " + dataPortName + "(0);\n"; + inputPortIndex += 1; + } + vhdlString += " else\n"; + vhdlString += + " " + guardPortName_ + "(i) <= " + + mainRegName_ + "(i)(0);\n"; + vhdlString += " end if;\n"; + + } else if (adfRF_->guardLatency() == 1) { + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = + static_cast(adfRF_->port(i)); + if (!adfPort->isInput()) { + continue; + } + vhdlString += + " " + guardPortName_ + "(i) <= " + + mainRegName_ + "(i)(0);\n"; + } + } else { + assert(false && "RFGen supports only guard latency 0 or 1"); + } + vhdlString += " end loop;\n"; + vhdlString += "END PROCESS guard_out_cp;\n"; + + inputPortIndex = 0; + std::string verilogString = + std::string(" always @* begin\n") + + " for (i = 0; i < " + std::to_string(adfRF_->size()) + + "; i = i + 1) begin\n"; + if (adfRF_->guardLatency() == 0) { + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = + static_cast(adfRF_->port(i)); + if (!adfPort->isInput()) { + continue; + } + std::string loadPortName = "load_" + adfPort->name() + "_in"; + std::string opcodePortName = "opcode_" + adfPort->name() + "_in"; + std::string dataPortName = "data_" + adfPort->name() + "_in"; + if (inputPortIndex == 0) { + verilogString += " if "; + } else { + verilogString += " end else if "; + } + verilogString += "(" + + loadPortName + " == '1 && i == " + + opcodePortName + ") begin\n"; + verilogString += + " " + guardPortName_ + "[i] <= " + dataPortName + "[0];\n"; + inputPortIndex += 1; + } + verilogString += " end else begin\n"; + verilogString += + " " + guardPortName_ + "[i] <= " + + mainRegName_ + "[i][0];\n"; + verilogString += " end\n"; + + } else if (adfRF_->guardLatency() == 1) { + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = + static_cast(adfRF_->port(i)); + if (!adfPort->isInput()) { + continue; + } + verilogString += + " " + guardPortName_ + "[i] <= " + + mainRegName_ + "[i][0];\n"; + } + } + verilogString += " end\n"; + verilogString += " end\n"; + + + behaviour_ << RawCodeLine(vhdlString, verilogString); +} + +/* + * Create register file write process. + */ +void +RFGen::createRFWriteProcess() { + // Write raw code as HDLGenerator does not yet support splicing or + // array types. + rf_ << RawCodeLine( + "type reg_type is array (natural range <>) of std_logic_vector(" + + std::to_string(adfRF_->width()) + "-1 downto 0 );", + "reg [" + std::to_string(adfRF_->width()) + "-1:0] " + + mainRegName_ + " [0:" + + std::to_string(adfRF_->size()) + "-1];"); + rf_ << RawCodeLine( + "signal " + mainRegName_ +" : reg_type (" + + std::to_string(adfRF_->size()) + "-1 downto 0);", + "integer i;\n"); + + std::string vhdlCode + = "---------------------------------------------------------------\n" + "Input : PROCESS (clk, rstx)\n" + "---------------------------------------------------------------\n" + "variable opc : integer;\n" + "\n" + "BEGIN\n" + " -- Asynchronous Reset\n" + " IF (rstx = '0') THEN\n" + " for idx in (" + mainRegName_ + "'length-1) downto 0 loop\n" + " " + mainRegName_ + "(idx) <= (others => '0');\n" + " end loop;\n" + " ELSIF (clk'EVENT AND clk = '1') THEN\n" + " IF glock_in = '0' THEN\n"; + + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = dynamic_cast(adfRF_->port(i)); + if (adfPort->isInput()) { + std::string loadPortName = "load_" + adfPort->name() + "_in"; + std::string opcodePortName = "opcode_" + adfPort->name() + "_in"; + std::string dataPortName = "data_" + adfPort->name() + "_in"; + vhdlCode + += " IF " + loadPortName + " = '1' THEN\n" + + " opc := to_integer(unsigned(" + opcodePortName + "));\n" + + " " + mainRegName_ + "(opc) <= " + dataPortName +";\n" + + " END IF;\n"; + } + } + + // If RF has zero register (first reg index always 0). + if (adfRF_->zeroRegister()) { + vhdlCode += " -- Zero register\n"; + vhdlCode += " " + mainRegName_ + "(0) <= (others => '0');\n"; + } + + vhdlCode += " END IF;\n"; + vhdlCode += " END IF;\n"; + vhdlCode += "END PROCESS Input;\n"; + + std::string verilogCode = ""; + verilogCode += std::string(" always @(posedge clk or negedge rstx) begin\n") + + " if (~rstx) begin\n" + + " for (i = 0; i < " + std::to_string(adfRF_->size()) + + "; i = i + 1) begin\n" + + " " + mainRegName_ + "[i] <= 0;\n" + + " end\n" + + " end else begin\n" + + " if (~glock_in) begin\n"; + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = dynamic_cast(adfRF_->port(i)); + if (adfPort->isInput()) { + std::string loadPortName = "load_" + adfPort->name() + "_in"; + std::string opcodePortName = "opcode_" + adfPort->name() + "_in"; + std::string dataPortName = "data_" + adfPort->name() + "_in"; + verilogCode += " if (" + loadPortName + " == 1) begin\n" + + " " + mainRegName_ + "[" + opcodePortName + "] <= " + + dataPortName + ";\n" + + " end\n"; + } + } + verilogCode += " end\n"; + // If RF has zero register (first reg index always 0). + if (adfRF_->zeroRegister()) { + verilogCode += mainRegName_ + "[0] <= 0;\n"; + } + verilogCode + += std::string(" end\n") + + "end\n" + + "\n"; + + behaviour_ << RawCodeLine(vhdlCode, verilogCode); +} + +/* + * Create register file read process. + */ +void +RFGen::createRFReadProcess() { + std::string vhdlCode = ""; + std::string verilogCode = "always @* begin\n"; + for (int i = 0; i < adfRF_->portCount(); ++i) { + TTAMachine::RFPort* adfPort = dynamic_cast(adfRF_->port(i)); + if (adfPort->isOutput()) { + std::string opcodePortName = "opcode_" + adfPort->name() + "_in"; + std::string dataPortName = "data_" + adfPort->name() + "_out"; + vhdlCode += + dataPortName + " <= " + mainRegName_ + "(to_integer(unsigned(" + + opcodePortName + ")));\n"; + verilogCode += dataPortName + " = " + mainRegName_ + + "[" + opcodePortName + "];\n"; + } + } + verilogCode += "end\n"; + + behaviour_ << RawCodeLine(vhdlCode, verilogCode); +} + +/* + * Create process to dump RF values into a file during simulation. + * + * Useful for debugging RISC-V machines, so create this + * only if RF has zero register. + */ +void +RFGen::createRFDumpProcess() { + if (!adfRF_->zeroRegister()) { + return; + } + std::string snippetPath = Environment::dataDirPath("RFGen"); + snippetPath += FileSystem::DIRECTORY_SEPARATOR; + std::string rfDumpSnippetVhdlFile = snippetPath + "vhdl/rf_dump_snippet.vhdl"; + std::string rfDumpSnippetVerilogFile = snippetPath + "verilog/rf_dump_snippet.v"; + std::deque dumpVhdlFileLines + = readFile(rfDumpSnippetVhdlFile); + std::deque dumpVerilogFileLines + = readFile(rfDumpSnippetVerilogFile); + assert(dumpVhdlFileLines.size() != 0 && + "vhdl file read unsuccesful"); + assert(dumpVerilogFileLines.size() != 0 && + "verilog file read unsuccesful"); + std::string rawVhdlCodeString = ""; + std::string rawVerilogCodeString = ""; + for (auto line: dumpVhdlFileLines) { + rawVhdlCodeString += std::string(line) + "\n"; + } + for (auto line: dumpVerilogFileLines) { + rawVerilogCodeString += std::string(line) + "\n"; + } + behaviour_ << RawCodeLine(rawVhdlCodeString, rawVerilogCodeString); + + std::string vhdlCode = ""; + TTAMachine::RFPort* firstWritePort = + static_cast(adfRF_->firstWritePort()); + std::string portName = firstWritePort->name(); + vhdlCode + += std::string("begin\n") + + " if start = true then\n" + + " file_open(rf_trace, \"rf.dump\", write_mode);\n" + + " start := false;\n" + + " end if;\n" + + " wait on clk until clk = '1' and clk'last_value = '0' and glock_in = '0';\n" + + " opc := to_integer(unsigned(opcode_" + portName + "_in));\n" + + " if(data_" + portName + "_in /= " + mainRegName_ + "(opc)) then\n" + + " if(load_" + portName + "_in = '1' and unsigned(opcode_" + + portName + "_in) /= to_unsigned(0, 32)) then\n" + + " write(line_out, reg_to_alias(opc));\n" + + " if(reg_to_alias(to_integer(unsigned(opcode_" + portName + + "_in))) = \"s10\" or reg_to_alias(opc) = \"s11\") then\n" + + "write(line_out, ' ');\n" + + " end if;\n" + + "write(line_out, to_unsigned_hex(" + mainRegName_ + +"(to_integer(unsigned(opcode_" + + portName + "_in)))));\n" + + "write(line_out, ' ');\n" + + "write(line_out, dash);\n" + + "write(line_out, '>');\n" + + "write(line_out, ' ');\n" + + "write(line_out, to_unsigned_hex(data_" + portName + "_in));\n" + + "writeline(rf_trace, line_out);\n" + + "end if;\n" + + "end if;\n" + + "end process file_output;\n" + + "--pragma translate_on\n"; + + std::string verilogCode = "// RFGen: RF dump verilog not yet implemented"; + + behaviour_ << RawCodeLine(vhdlCode, verilogCode); +} + +void +RFGen::finalizeHDL() { + // Finalize and set global options. + rf_ << behaviour_; + for (auto&& option : globalOptions_) { + rf_ << Option(option); + } +} + +/** + * + * Generate all RFGen RFs. + * + */ +void +RFGen::implement( + const ProGeOptions& options, std::vector globalOptions, + const std::vector& generatetRFs, + const TTAMachine::Machine& machine, ProGe::NetlistBlock* core) { + + // Generate RF innards. + for (auto rfg : generatetRFs) { + RFGen rfgen(options, globalOptions, rfg, machine, core); + rfgen.createRFHeaderComment(); + rfgen.createMandatoryPorts(); + rfgen.createGuardPort(); + rfgen.createRFWriteProcess(); + rfgen.createRFReadProcess(); + rfgen.createRFDumpProcess(); + rfgen.createGuardProcess(); + + rfgen.finalizeHDL(); + rfgen.createImplementationFiles(); + } +} diff --git a/openasip/src/applibs/HWGen/RFGen.hh b/openasip/src/applibs/HWGen/RFGen.hh new file mode 100644 index 0000000000..0c8f7c93fb --- /dev/null +++ b/openasip/src/applibs/HWGen/RFGen.hh @@ -0,0 +1,99 @@ +/* + 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 RFGen.hh +* +* Register file generator. +* +* @author Joonas Multanen 2024 (joonas.multanen-no-spam-tuni.fi) +* @note rating: red +*/ + +#pragma once + +#include "RFGenerated.hh" +#include "HDLGenerator.hh" +#include "Machine.hh" +#include "NetlistBlock.hh" +#include "ProGeOptions.hh" +#include +#include +#include "RFImplementationLocation.hh" +#include "BinaryOps.hh" + +class RFGen { +public: + RFGen() = delete; + RFGen(const RFGen&) = delete; + RFGen(const ProGeOptions& options, + std::vector globalOptions, IDF::RFGenerated& rfg, + const TTAMachine::Machine& machine, ProGe::NetlistBlock* core): + options_(options), + globalOptions_(globalOptions), + rfg_(rfg), + core_(core), + rf_(StringTools::stringToLower("rf_" + rfg.name())), + adfRF_(machine.registerFileNavigator().item(rfg.name())), + moduleName_("rf_" + rfg_.name()) { + + // Find the netlistblock + for (size_t i = 0; i < core_->subBlockCount(); ++i) { + std::string name = core_->subBlock(i).moduleName(); + if (name == StringTools::stringToLower(moduleName_)) { + netlistBlock_ = &core_->subBlock(i); + break; + } + } + } + + static void implement(const ProGeOptions& options, + std::vector globalOptions, + const std::vector& generatetRFs, + const TTAMachine::Machine& machine, ProGe::NetlistBlock* core); + +private: + std::deque readFile(std::string filename); + std::string findAbsolutePath(std::string file); + void createRFHeaderComment(); + void createMandatoryPorts(); + void createGuardPort(); + void createGuardProcess(); + void createRFWriteProcess(); + void createRFReadProcess(); + void createRFDumpProcess(); + void finalizeHDL(); + void createImplementationFiles(); + + const ProGeOptions& options_; + std::vector globalOptions_; + + IDF::RFGenerated& rfg_; + ProGe::NetlistBlock* core_; + + HDLGenerator::Module rf_; + TTAMachine::RegisterFile* adfRF_; + + std::string moduleName_; + ProGe::NetlistBlock* netlistBlock_; + + HDLGenerator::Behaviour behaviour_; + + const std::string mainRegName_ = "regfile_r"; + const std::string guardPortName_ = "guard_out"; +}; diff --git a/openasip/src/applibs/HWGen/WidthTransformations.hh b/openasip/src/applibs/HWGen/WidthTransformations.hh index 4e6b391f79..bac0fb4c61 100755 --- a/openasip/src/applibs/HWGen/WidthTransformations.hh +++ b/openasip/src/applibs/HWGen/WidthTransformations.hh @@ -100,13 +100,13 @@ namespace HDLGenerator { int signalWidth) { std::string sigWidth = std::to_string(signalWidth); vhdl_ = "((" + extWidth + "-1 downto " + sigWidth + " => " + name; - verilog_ = "{{" + extWidth + "-" + sigWidth + "{" + name + "}"; + verilog_ = "{{" + extWidth + "{" + name + "}"; if (signalWidth > 1) { vhdl_ += "(" + sigWidth + "-1)"; verilog_ += "[" + sigWidth + "-1]"; } vhdl_ += ") & " + name + ")"; - verilog_ += "}}" + name + "}"; + verilog_ += "}}"; } }; } diff --git a/openasip/src/applibs/Makefile.am b/openasip/src/applibs/Makefile.am index 4f14bcd150..0bf7d9fce4 100644 --- a/openasip/src/applibs/Makefile.am +++ b/openasip/src/applibs/Makefile.am @@ -1,7 +1,7 @@ SUBDIRS = Interpreter Simulator osal TraceDB \ bem PIG Assembler hdb Scheduler Estimator ProGe EPSGenerator mach idf \ costdb program FSA Explorer dsdb LLVMBackend ImplementationTester \ -PlatformIntegrator HWGen +PlatformIntegrator TestGenerator HWGen noinst_LTLIBRARIES = libapplibs.la @@ -29,6 +29,7 @@ libapplibs_la_LIBADD = \ LLVMBackend/libopenasipllvmbackend.la \ ImplementationTester/libimpltester.la \ PlatformIntegrator/libplatformintegrator.la \ + TestGenerator/libtege.la \ HWGen/libhwgen.la diff --git a/openasip/src/applibs/PIG/ProgramImageGenerator.cc b/openasip/src/applibs/PIG/ProgramImageGenerator.cc index d5a0322b02..a0c2d28f7f 100644 --- a/openasip/src/applibs/PIG/ProgramImageGenerator.cc +++ b/openasip/src/applibs/PIG/ProgramImageGenerator.cc @@ -773,8 +773,7 @@ const std::string ProgramImageGenerator::DATA_START_DESC( bool ProgramImageGenerator::isDataStartSet(std::string aSpace) const { CmdLineOptionParser* opts = dataStartOptions_; - assert(opts); - if (opts->isDefined()) { + if (opts != nullptr && opts->isDefined()) { int size = opts->listSize(); if (size == 1) { return true; diff --git a/openasip/src/applibs/PIG/ProgramImageGenerator.hh b/openasip/src/applibs/PIG/ProgramImageGenerator.hh index d42031a466..d9e7c2a536 100644 --- a/openasip/src/applibs/PIG/ProgramImageGenerator.hh +++ b/openasip/src/applibs/PIG/ProgramImageGenerator.hh @@ -137,7 +137,7 @@ private: std::string entityName_; static const std::string DATA_START_DESC; - CmdLineOptionParser* dataStartOptions_; + CmdLineOptionParser* dataStartOptions_ = nullptr; /// The plugin tool. PluginTools pluginTool_; diff --git a/openasip/src/applibs/ProGe/GeneratableRFNetlistBlock.hh b/openasip/src/applibs/ProGe/GeneratableRFNetlistBlock.hh new file mode 100755 index 0000000000..bbf87702b4 --- /dev/null +++ b/openasip/src/applibs/ProGe/GeneratableRFNetlistBlock.hh @@ -0,0 +1,83 @@ +/* + Copyright (c) 2002-2024 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. + */ +/** + * @file GeneratableRFNetlistBlock.hh + * + * Declaration of GeneratableRFNetlistBlock. + * + * @author Lasse Lehtonen 2017 (lasse.lehtonen-no.spam-tut.fi) + * @author Joonas Multanen 2024 (joonas.multanen-no.spam-tuni.fi) + */ + +#pragma once + +#include "RFPort.hh" +#include "NetlistBlock.hh" +#include "NetlistGenerator.hh" +#include "MathTools.hh" + +namespace ProGe { + +class GeneratableRFNetlistBlock : public NetlistBlock { +public: + GeneratableRFNetlistBlock(const std::string& moduleName, + const std::string instanceName, NetlistGenerator& generator) + : NetlistBlock(moduleName, instanceName), generator_(generator) {} + + virtual ~GeneratableRFNetlistBlock() = default; + + void addInOperand(TTAMachine::RFPort* port, int id, int numRegisters) { + (void) id; + NetlistPort* dataPort = + new NetlistPort("data_" + port->name() + "_in", + std::to_string(port->width()), BIT_VECTOR, IN, *this); + NetlistPort* loadPort = new NetlistPort( + "load_" + port->name() + "_in", "1", BIT, IN, *this); + generator_.mapLoadPort(*dataPort, *loadPort); + generator_.mapNetlistPort(*port, *dataPort); + NetlistPort* opcodePort = + new NetlistPort("opcode_" + port->name() + "_in", + MathTools::requiredBits(numRegisters - 1), + BIT_VECTOR, IN, *this); + generator_.mapRFOpcodePort(*dataPort, *opcodePort); + } + void addOutOperand(TTAMachine::RFPort* port, int numRegisters) { + NetlistPort* dataPort = + new NetlistPort("data_" + port->name() + "_out", + std::to_string(port->width()), BIT_VECTOR, OUT, *this); + NetlistPort* loadPort = new NetlistPort( + "load_" + port->name() + "_in", "1", BIT, IN, *this); + generator_.mapLoadPort(*dataPort, *loadPort); + generator_.mapNetlistPort(*port, *dataPort); + NetlistPort* opcodePort = + new NetlistPort("opcode_" + port->name() + "_in", + MathTools::requiredBits(numRegisters - 1), + BIT_VECTOR, IN, *this); + generator_.mapRFOpcodePort(*dataPort, *opcodePort); + } + +private: + NetlistGenerator& generator_; +}; +} diff --git a/openasip/src/applibs/ProGe/Makefile.am b/openasip/src/applibs/ProGe/Makefile.am index f28932e0b5..99954604be 100644 --- a/openasip/src/applibs/ProGe/Makefile.am +++ b/openasip/src/applibs/ProGe/Makefile.am @@ -76,6 +76,7 @@ libproge_la_SOURCES += \ ProcessorWrapperBlock.hh LoopBufferBlock.hh \ MemoryBusInterface.hh \ GeneratableFUNetlistBlock.hh \ + GeneratableRFNetlistBlock.hh \ OperationPool.hh Operation.hh Operand.hh \ IPXact.hh ProGeCmdLineOptions.hh ProGeTools.hh \ MicroCodeGenerator.hh RV32MicroCodeGenerator.hh \ diff --git a/openasip/src/applibs/ProGe/NetlistGenerator.cc b/openasip/src/applibs/ProGe/NetlistGenerator.cc index 6dae83650c..85dd089a51 100644 --- a/openasip/src/applibs/ProGe/NetlistGenerator.cc +++ b/openasip/src/applibs/ProGe/NetlistGenerator.cc @@ -81,6 +81,7 @@ #include "MemoryBusInterface.hh" #include "GeneratableFUNetlistBlock.hh" +#include "GeneratableRFNetlistBlock.hh" #include "ProGeTools.hh" using namespace IDF; @@ -197,8 +198,8 @@ namespace ProGe { // add toplevel block coreBlock_ = new NetlistBlock(entityNameStr, "tta_core"); - coreBlock_->addPackage(context_.coreEntityName() + "_globals"); coreBlock_->addPackage(context_.coreEntityName() + "_imem_mau"); + coreBlock_->addPackage(context_.coreEntityName() + "_globals"); // add GCU to the netlist addGCUToNetlist(*coreBlock_, imemWidthInMAUs); @@ -215,6 +216,11 @@ namespace ProGe { addFUToNetlist(location, *coreBlock_, warningStream); } + // add Generated RFs to the toplevel netlist. + for (auto rfg : context_.idf().RFGenerations()) { + addGeneratableRFsToNetlist(rfg, *coreBlock_); + } + // add register files to the netlist for (int i = 0; i < context_.idf().rfImplementationCount(); i++) { RFImplementationLocation& location = @@ -682,6 +688,17 @@ namespace ProGe { } } + /** + * Returns true if there is an entry for the RF name given as parameter. + * + * @param rfName Name of the RF in ADF. + * @return True if RF entry exists, false otherwise. + */ + bool + NetlistGenerator::rfHasEntry(const std::string& rfName) const { + return MapTools::containsKey(rfEntryMap_, rfName) ? true : false; + } + /** * Adds the global control unit to the netlist as sub-block of the given * top-level block. @@ -957,6 +974,65 @@ namespace ProGe { return block; } + GeneratableRFNetlistBlock* NetlistGenerator::addGeneratableRFsToNetlist( + const IDF::RFGenerated& rfg, NetlistBlock& coreBlock) { + + std::string rfName = "rf_" + rfg.name(); + std::transform(rfName.begin(), rfName.end(), rfName.begin(), ::tolower); + + GeneratableRFNetlistBlock* block = new GeneratableRFNetlistBlock( + rfName, rfName + "_generated", *this); + coreBlock.addSubBlock(block); + + RegisterFile* rf = + context_.adf().registerFileNavigator().item(rfg.name()); + + mapNetlistBlock(*rf, *block); + + // add clock port + NetlistPort* clkPort = new NetlistPort("clk", "1", 1, BIT, IN, *block); + mapClockPort(*block, *clkPort); + // connect clock port + NetlistPort& tlClkPort = this->clkPort(coreBlock); + coreBlock.netlist().connect(*clkPort, tlClkPort); + + // add reset port + NetlistPort* rstPort = new NetlistPort("rstx", "1", 1, BIT, IN, *block); + mapResetPort(*block, *rstPort); + // connect reset port + NetlistPort& tlRstPort = this->rstPort(coreBlock); + coreBlock.netlist().connect(*rstPort, tlRstPort); + + // add global lock port + NetlistPort* glockPort = + new NetlistPort("glock_in", "1", 1, BIT, IN, *block); + mapGlobalLockPort(*block, *glockPort); + + // operand ports. + for (int i = 0; i < rf->portCount(); ++i) { + RFPort* adfPort = dynamic_cast(rf->port(i)); + + if (adfPort->isInput()) { + block->addInOperand(adfPort, i, rf->numberOfRegisters()); + } else { + block->addOutOperand(adfPort, rf->numberOfRegisters()); + } + } + + // add guard port + if (rf->isUsedAsGuard()) { + string guardPortName = "guard_out"; + string size; + size = Conversion::toString(rf->numberOfRegisters()); + NetlistPort* guardPort = new NetlistPort(guardPortName, size, + BIT_VECTOR, OUT, *block); + mapRFGuardPort(*block, *guardPort); + } + + return block; + } + + /** * Adds the FU identified by the given FUImplementationLocation * instance to the netlist. diff --git a/openasip/src/applibs/ProGe/NetlistGenerator.hh b/openasip/src/applibs/ProGe/NetlistGenerator.hh index 538913729f..add42de004 100644 --- a/openasip/src/applibs/ProGe/NetlistGenerator.hh +++ b/openasip/src/applibs/ProGe/NetlistGenerator.hh @@ -48,6 +48,7 @@ namespace IDF { class MachineImplementation; class FUGenerated; + class RFGenerated; } namespace HDB { @@ -77,12 +78,14 @@ namespace ProGe { class ProGeContext; class Signal; class GeneratableFUNetlistBlock; + class GeneratableRFNetlistBlock; /** * Generates a netlist of processor building blocks. [DEPRECATED] */ class NetlistGenerator { friend class GeneratableFUNetlistBlock; + friend class GeneratableRFNetlistBlock; public: NetlistGenerator( @@ -121,6 +124,7 @@ namespace ProGe { HDB::FUEntry& fuEntry(const std::string& fuName) const; HDB::RFEntry& rfEntry(const std::string& rfName) const; + bool rfHasEntry(const std::string& rfName) const; const ProGeContext& context() const { return context_; } @@ -181,6 +185,8 @@ namespace ProGe { NetlistBlock& netlistBlock, std::ostream& warningStream); GeneratableFUNetlistBlock* addGeneratableFUsToNetlist( const IDF::FUGenerated& fug, NetlistBlock& netlistBlock); + GeneratableRFNetlistBlock* addGeneratableRFsToNetlist( + const IDF::RFGenerated& rfg, NetlistBlock& netlistBlock); void addFUExternalPortsToNetlist( const HDB::FUImplementation& fuImplementation, NetlistBlock& coreBlock, NetlistBlock& fuBlock, diff --git a/openasip/src/applibs/ProGe/ProGeOptions.hh b/openasip/src/applibs/ProGe/ProGeOptions.hh index ce6410d68a..139cd9b848 100644 --- a/openasip/src/applibs/ProGe/ProGeOptions.hh +++ b/openasip/src/applibs/ProGe/ProGeOptions.hh @@ -71,7 +71,8 @@ struct ProGeOptions { resetAllRegisters(cmd.resetAllRegisters()), fuBackRegistered(cmd.fuBackRegistered()), fuFrontRegistered(cmd.fuFrontRegistered()), - fuMiddleRegistered(cmd.fuMiddleRegistered()) { + fuMiddleRegistered(cmd.fuMiddleRegistered()), + dontCareInitialization(cmd.dontCareInitialization()) { validate(); } @@ -109,6 +110,7 @@ struct ProGeOptions { std::vector fuBackRegistered; std::vector fuFrontRegistered; std::vector fuMiddleRegistered; + bool dontCareInitialization; void validate() { diff --git a/openasip/src/applibs/ProGe/ProGeScriptGenerator.cc b/openasip/src/applibs/ProGe/ProGeScriptGenerator.cc index 27bcb003d7..be0478d834 100644 --- a/openasip/src/applibs/ProGe/ProGeScriptGenerator.cc +++ b/openasip/src/applibs/ProGe/ProGeScriptGenerator.cc @@ -187,7 +187,7 @@ ProGeScriptGenerator::generateModsimCompile() { string coverageOptAssign = " $coverage_opt"; string program = ((language_==VHDL)? - "vcom": "vlog +define+SIMTIME=" + simulationRuntime_ + + "vcom": "vlog -sv +define+SIMTIME=" + simulationRuntime_ + " +incdir+verilog +incdir+gcu_ic +incdir+tb"); string exitOnFailure = "|| exit 1"; string checkSynthesisFLag = (language_==VHDL)?" -check_synthesis":""; @@ -296,7 +296,7 @@ ProGeScriptGenerator::generateIverilogCompile() { stream << "rm -rf " << testbenchName_ << endl << endl - << "iverilog -g2001 -D _IVERILOG_ " + << "iverilog -g2012 -D _IVERILOG_ " << "-D SIMTIME=" << simulationRuntime_ << " " << "-Itb -Iverilog -Igcu_ic "; outputScriptCommands(stream, vhdlFiles_, ""," \\"); @@ -398,10 +398,8 @@ ProGeScriptGenerator::generateIverilogSimulate() { << endl; stream << "fi" << endl; - stream << "./" << testbenchName_ - << " --assert-level=none --stop-time=" - << "${runtime}" << "ns" - << endl; + stream << "./" << testbenchName_ + << " --assert-level=none" << endl; stream.close(); } diff --git a/openasip/src/applibs/ProGe/ProGeUI.cc b/openasip/src/applibs/ProGe/ProGeUI.cc index d1ed846d47..c2f2eeb41b 100644 --- a/openasip/src/applibs/ProGe/ProGeUI.cc +++ b/openasip/src/applibs/ProGe/ProGeUI.cc @@ -380,16 +380,7 @@ ProGeUI::generateTestBench( checkIfNull(machine_, "ADF not loaded"); checkIfNull(idf_, "IDF not loaded"); - if (language == Verilog) { - // Note: deprecated legacy test bench. Still used since the new test - // bench does not yet support generation for verilog. - ProGeTestBenchGenerator tbGen = ProGeTestBenchGenerator(); - tbGen.generate( - language, *machine_, *idf_, dstDir, progeOutDir, entityName_); - } else { - // New test bench generation to be replacing the old one. WIP and - // first used to generate test benches for TTA processors. - try { + try { TestBenchBlock coreTestbench( generator_.generatorContext(), generator_.processorTopLevel()); @@ -401,7 +392,6 @@ ProGeUI::generateTestBench( tbGen.generate( language, *machine_, *idf_, dstDir, progeOutDir, entityName_); } - } } /** @@ -536,6 +526,7 @@ ProGeUI::generateIDF( } std::vector handledFUs; + std::vector handledRFs; // Prefill the handledFUs with values from the .idf-file for (auto&& fu : machine_->functionUnitNavigator()) { @@ -545,6 +536,14 @@ ProGeUI::generateIDF( } } + // Prefill the handledRFs with values from the .idf-file + for (auto&& rf : machine_->registerFileNavigator()) { + if (idf_->hasRFImplementation(rf->name()) || + idf_->hasRFGeneration(rf->name())) { + handledRFs.emplace_back(rf->name()); + } + } + std::vector infos = ProGeTools::createFUGeneratableOperationInfos(options, verbose); //! Checking only operations that ADF has instead of them all @@ -552,14 +551,15 @@ ProGeUI::generateIDF( std::vector dagops = ProGeTools::generateableDAGOperations(infos, verbose); - auto already_handled = [&](const std::string& name) { - return std::find(handledFUs.begin(), handledFUs.end(), name) != - handledFUs.end(); + auto already_handled = [&](const std::string& name, + const std::vector handledEntries) { + return std::find(handledEntries.begin(), handledEntries.end(), name) != + handledEntries.end(); }; - auto select_hdb_implementations = [&]() { + auto select_fu_hdb_implementations = [&]() { for (auto&& fu : machine_->functionUnitNavigator()) { - if (already_handled(fu->name())) { + if (already_handled(fu->name(), handledFUs)) { continue; } verbose << " select implementation for " << fu->name() << "... "; @@ -577,9 +577,9 @@ ProGeUI::generateIDF( } }; - auto generate_implementations = [&]() { + auto generate_fu_implementations = [&]() { for (auto&& fu : machine_->functionUnitNavigator()) { - if (already_handled(fu->name())) { + if (already_handled(fu->name(), handledFUs)) { continue; } verbose << " generate implementation for " << fu->name() @@ -598,11 +598,11 @@ ProGeUI::generateIDF( // Create or select FUs. if (options.preferHDLGeneration) { - generate_implementations(); - select_hdb_implementations(); + generate_fu_implementations(); + select_fu_hdb_implementations(); } else { - select_hdb_implementations(); - generate_implementations(); + select_fu_hdb_implementations(); + generate_fu_implementations(); } // IUs. @@ -622,21 +622,17 @@ ProGeUI::generateIDF( } } - // RFs. for (auto&& rf : machine_->registerFileNavigator()) { - if (idf_->hasRFImplementation(rf->name())) { + if (already_handled(rf->name(), handledRFs)) { continue; } - verbose << " select implementation for " << rf->name() << "... "; - RFImplementationLocation* loc = - new RFImplementationLocation("", -1, rf->name()); - if (ProGeTools::checkForSelectableRF(options, *rf, *loc, verbose)) { - verbose << "OK (selected " << loc->id() << " from " - << loc->hdbFile() << ")\n"; - idf_->addRFImplementation(loc); - } else { - verbose << "FAIL\n"; - } + verbose << " generate implementation for " << rf->name() + << "... "; + IDF::RFGenerated rfg(rf->name()); + // Assume that we can always generate the missing RFs. + verbose << "OK\n"; + idf_->addRFGeneration(rfg); + handledRFs.emplace_back(rfg.name()); } // IC/Decoder plugin. diff --git a/openasip/src/applibs/ProGe/ProcessorGenerator.cc b/openasip/src/applibs/ProGe/ProcessorGenerator.cc index 5d949f18bc..fe07ebd880 100644 --- a/openasip/src/applibs/ProGe/ProcessorGenerator.cc +++ b/openasip/src/applibs/ProGe/ProcessorGenerator.cc @@ -92,6 +92,7 @@ #include "ProGeOptions.hh" #include "FUGen.hh" +#include "RFGen.hh" using boost::format; using std::endl; @@ -179,6 +180,11 @@ ProcessorGenerator::generateProcessor( options, globalOptions, generatorContext_->idf().FUGenerations(), generatorContext_->adf(), coreTopBlock_); + // Generate generatable RF implementations. + RFGen::implement( + options, globalOptions, generatorContext_->idf().RFGenerations(), + generatorContext_->adf(), coreTopBlock_); + coreTopBlock_->write(Path(options.outputDirectory), options.language); string topLevelDir = options.outputDirectory + @@ -285,7 +291,10 @@ ProcessorGenerator::generateGlobalsPackage( << "parameter IMEMWIDTHINMAUS = " << imemWidthInMAUs << "," << endl << "// clock period" << endl - << "parameter PERIOD = 10," << endl; // 10 will equal 10ns + << "parameter PERIOD = 10," << endl // 10 will equal 10ns + << "// instruction width" << endl + << "parameter IMEMDATAWIDTH = IMEMWIDTHINMAUS*IMEMMAUWIDTH," + << endl; plugin.writeGlobalDefinitions(ProGe::Verilog, stream); diff --git a/openasip/src/applibs/ProGe/ProcessorWrapperBlock.cc b/openasip/src/applibs/ProGe/ProcessorWrapperBlock.cc index 5b8de5f881..ba196a258e 100644 --- a/openasip/src/applibs/ProGe/ProcessorWrapperBlock.cc +++ b/openasip/src/applibs/ProGe/ProcessorWrapperBlock.cc @@ -66,7 +66,6 @@ ProcessorWrapperBlock::ProcessorWrapperBlock( // Instantiate core // addSubBlock(coreBlock_); - // Memory instantiations and connections // for (size_t i = 0; i < coreBlock_->portGroupCount(); i++) { const NetlistPortGroup& portGrp = coreBlock_->portGroup(i); @@ -88,17 +87,13 @@ ProcessorWrapperBlock::ProcessorWrapperBlock( connectResets(); connectLockStatus(*coreLocked); - // Package holding instruction bus constants - std::set procPackages{context.globalPackage().name()}; - // Other packages possibly holding constants used in core ports. for (size_t i = 0; i < processorBlock.packageCount(); i++) { - procPackages.insert(processorBlock.package(i)); + addPackage(processorBlock.package(i)); } - for (auto packageStr : procPackages) { - addPackage(packageStr); - } + // Package holding instruction bus constants + addPackage(context.globalPackage().name()); addPackage(context.coreEntityName() + "_params"); @@ -180,7 +175,6 @@ ProcessorWrapperBlock::addDataMemory(const MemoryBusInterface& coreDmemPort) { const NetlistPort& addrPort = coreDmemPort.portBySignal(SigT::ADDRESS); const NetlistPort& dataPort = coreDmemPort.portBySignal(SigT::WRITE_DATA); - SinglePortSSRAMBlock* dmemBlock = new SinglePortSSRAMBlock( addrPort.widthFormula(), dataPort.widthFormula(), TCEString("tb/dmem_") + coreDmemPort.addressSpace() + "_init.img", diff --git a/openasip/src/applibs/ProGe/SinglePortByteMaskSSRAMBlock.cc b/openasip/src/applibs/ProGe/SinglePortByteMaskSSRAMBlock.cc index 1dfeb2d2f4..59a60ac208 100644 --- a/openasip/src/applibs/ProGe/SinglePortByteMaskSSRAMBlock.cc +++ b/openasip/src/applibs/ProGe/SinglePortByteMaskSSRAMBlock.cc @@ -110,13 +110,16 @@ SinglePortByteMaskSSRAMBlock::write( const Path& targetBaseDir, HDL targetLang) const { Path progeDataDir(Environment::dataDirPath("ProGe")); - assert(targetLang == VHDL); + assert(targetLang == VHDL || targetLang == Verilog); - std::string tempFile = std::string("synch_byte_mask_sram.vhdl"); + std::string tempFile = (targetLang == VHDL) + ? std::string("synch_byte_mask_sram.vhdl") + : std::string("synch_byte_mask_sram.v"); std::string targetDir = (isForSimulation_) ? std::string("tb") : ((targetLang == VHDL) ? std::string("vhdl") : std::string("verilog")); + HDLTemplateInstantiator().instantiateTemplateFile( (progeDataDir / "tb" / tempFile).string(), (targetBaseDir / targetDir / tempFile).string()); diff --git a/openasip/src/applibs/ProGe/TestBenchBlock.cc b/openasip/src/applibs/ProGe/TestBenchBlock.cc index 7419c6c786..46c6a7b3e0 100644 --- a/openasip/src/applibs/ProGe/TestBenchBlock.cc +++ b/openasip/src/applibs/ProGe/TestBenchBlock.cc @@ -102,16 +102,14 @@ TestBenchBlock::~TestBenchBlock() { void TestBenchBlock::write(const Path& targetBaseDir, HDL targetLang) const { // Check language compatibility // - if (targetLang != VHDL) { - THROW_EXCEPTION(NotAvailable, "Only VHDL is supported."); + if (targetLang != VHDL && targetLang != Verilog) { + THROW_EXCEPTION(NotAvailable, "Only VHDL and Verilog are supported."); } - HDLTemplateInstantiator instantiator(context_.coreEntityName()); - instantiator.replacePlaceholder("dut-entity", proc_->moduleName()); - if (context_.idf().icDecoderParameterValue("debugger") == "external") { - instantiator.replacePlaceholder("proc-entity-db-signals", + if (targetLang == VHDL) { + instantiator.replacePlaceholder("proc-entity-db-signals", "db_pc_start : in std_logic_vector(IMEMADDRWIDTH-1 downto 0);\n" "db_tta_nreset : in std_logic;\n" "db_lockrq : in std_logic;"); @@ -120,18 +118,25 @@ TestBenchBlock::write(const Path& targetBaseDir, HDL targetLang) const { "db_pc_start => (others => '0'),\n" "db_tta_nreset => '1',\n" "db_lockrq => '0',"); + } else { + THROW_EXCEPTION(NotAvailable, "Only VHDL is supported external" + "debugger generation."); + } } - FileSystem::createDirectory(targetBaseDir/"tb"); - Path progeDataDir(Environment::dataDirPath("ProGe")); - instantiator.instantiateTemplateFile( - progeDataDir/"tb"/"testbench.vhdl.tmpl", - targetBaseDir/"tb"/"testbench.vhdl"); - instantiator.instantiateTemplateFile( - progeDataDir/"tb"/"clkgen.vhdl", - targetBaseDir/"tb"/"clkgen.vhdl"); - + if (targetLang == VHDL) { + instantiator.instantiateTemplateFile( + progeDataDir/"tb"/"testbench.vhdl.tmpl", + targetBaseDir/"tb"/"testbench.vhdl"); + instantiator.instantiateTemplateFile( + progeDataDir/"tb"/"clkgen.vhdl", + targetBaseDir/"tb"/"clkgen.vhdl"); + } else { + instantiator.instantiateTemplateFile( + progeDataDir/"tb"/"testbench.v.tmpl", + targetBaseDir/"tb"/"testbench.v"); + } proc_->write(targetBaseDir, targetLang); } diff --git a/openasip/src/applibs/ProGe/VHDLNetlistWriter.cc b/openasip/src/applibs/ProGe/VHDLNetlistWriter.cc index da44c209ea..24434a8906 100644 --- a/openasip/src/applibs/ProGe/VHDLNetlistWriter.cc +++ b/openasip/src/applibs/ProGe/VHDLNetlistWriter.cc @@ -631,7 +631,6 @@ VHDLNetlistWriter::writePortMappings( const NetlistPort* dstPort = block.netlist()[dstVertex]; PortConnectionProperty property = block.netlist()[edgeDescriptor]; - if (&dstPort->parentBlock() == &block) { if (port.dataType() != dstPort->dataType()) { int index = 0; @@ -668,6 +667,7 @@ VHDLNetlistWriter::writePortMappings( dstConn = portSignalName(port); } } else { + dstConn = portSignalName(port); } stream << indentation(3) << srcConn << " => " << dstConn; diff --git a/openasip/src/applibs/ProGe/VerilogNetlistWriter.cc b/openasip/src/applibs/ProGe/VerilogNetlistWriter.cc index 62d3b4ffd9..6c32fc6b99 100644 --- a/openasip/src/applibs/ProGe/VerilogNetlistWriter.cc +++ b/openasip/src/applibs/ProGe/VerilogNetlistWriter.cc @@ -57,6 +57,7 @@ using std::pair; const std::string GROUND_SIGNAL = "ground_signal"; const std::string PARAM_STRING = "string"; +const std::string PARAM_BOOLEAN = "boolean"; namespace ProGe { @@ -107,13 +108,18 @@ VerilogNetlistWriter::writeNetlistParameterPackage( netlistParameterPkgName() + "_pkg.vh"; ofstream outFile; outFile.open(fileName.c_str(), ofstream::out); - 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; + if (targetNetlistBlock().netlist().parameterCount() == 0) { + outFile << "parameter " << targetNetlistBlock().moduleName() + << "_DUMMY " << " = 0" << endl; + } else { + 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; + } } } @@ -171,14 +177,14 @@ VerilogNetlistWriter::writeBlock( separator = ","; } + // create generics + writeGenericDeclaration(block, 1, indentation(1), outFile); + outFile << ")" << endl; - + // create port declarations writePortDeclaration(block, 1, indentation(1), outFile); outFile << endl; - - // create generics - writeGenericDeclaration(block, 1, indentation(1), outFile); // create architecture writeSignalDeclarations(block, outFile); outFile << endl; @@ -204,6 +210,9 @@ 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) { + stream << ","; + } stream << endl; for (size_t i = 0; i < block.parameterCount(); i++) { stream << generateIndentation(indentationLevel, indentation) @@ -213,7 +222,16 @@ VerilogNetlistWriter::writeGenericDeclaration( << param.name(); if (param.defaultValue() != "") { stream << " = "; - if (param.type().lower() == PARAM_STRING) { + if (param.type().lower() == PARAM_BOOLEAN) { + if (param.defaultValue() == "false") { + stream << "0"; + } else if (param.defaultValue() == "true") { + stream << "1"; + } else { + 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 if (!param.defaultValue().startsWith("\"")) stream << "\""; @@ -223,7 +241,9 @@ VerilogNetlistWriter::writeGenericDeclaration( stream << param.defaultValue(); } } - stream << ";" << endl; + if (i != block.parameterCount()-1) { + stream << "," << endl; + } } } } @@ -503,8 +523,16 @@ VerilogNetlistWriter::writePortMappings( for (size_t i = 0; i < component.parameterCount(); i++) { Parameter param = component.parameter(i); stream << indentation(2) << "." << param.name() << "("; - - if (param.type().lower() == PARAM_STRING) { + if (param.type().lower() == PARAM_BOOLEAN) { + if (param.defaultValue() == "false") { + stream << "0"; + } else if (param.defaultValue() == "true") { + stream << "1"; + } else { + 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()); } else { stream << param.value(); @@ -534,25 +562,53 @@ VerilogNetlistWriter::writePortMappings( edge_descriptor edgeDescriptor = *edges.first; vertex_descriptor dstVertex = boost::target(edgeDescriptor, block.netlist()); - NetlistPort* dstPort = block.netlist()[dstVertex]; - + const NetlistPort* dstPort = block.netlist()[dstVertex]; + PortConnectionProperty property = + block.netlist()[edgeDescriptor]; if (&dstPort->parentBlock() == &block) { if (port.dataType() != dstPort->dataType()) { + int index = 0; + 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() + "[0]"; + dstConn = dstPort->name() + "[" + + Conversion::toString(index) + "]"; } else { + assert(dstPort->dataType() == BIT); - srcConn += "[0]"; - dstConn = dstPort->name(); + if (port.widthFormula() == "1") { + srcConn += "[0]"; + dstConn = dstPort->name(); + } else { + dstConn = portSignalName(port); + } } } else { - dstConn = dstPort->name(); + + 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 << ")"; @@ -601,6 +657,15 @@ VerilogNetlistWriter::isNumber(const std::string& formula) { return true; } +/** + * Returns true if port uses single parameter of its parent block as port + * width. + */ +bool +VerilogNetlistWriter::usesParameterWidth(const NetlistPort& port) { + const BaseNetlistBlock& parent = port.parentBlock(); + return parent.hasParameter(port.widthFormula()); +} /** * Returns a string which makes indetation of the given level. @@ -641,10 +706,13 @@ std::string VerilogNetlistWriter::portSignalName(const NetlistPort& port) { const BaseNetlistBlock* parentBlock = &port.parentBlock(); if (port.hasStaticValue()) { - return "{" + Conversion::toString(port.realWidth()) + "{" + - ((port.staticValue().is(StaticSignal::VCC)) ? "1'b1" - : "1'b0") + - "}}"; + std::string portWidth; + if (port.realWidthAvailable()) { + portWidth = Conversion::toString(port.realWidth()); + } else { + portWidth = port.widthFormula(); + } + return ((port.staticValue().is(StaticSignal::VCC)) ? "'1" : "'0"); } return parentBlock->instanceName() + "_" + port.name() +"_wire"; } @@ -662,12 +730,15 @@ 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]"; + return " [0:0]"; + } else if (usesParameterWidth(port)) { + return " [" + parameterWidthValue(port) + + "-1 :0]"; } else { - return "[" + port.widthFormula()+"-1: 0]"; + return " [ " + port.widthFormula()+"-1: 0]"; } } } @@ -690,4 +761,14 @@ VerilogNetlistWriter::genericMapStringValue(const TCEString& generic) const { } return generic; } + +/** + * Returns port width value of port that uses parameter as width. + */ +TCEString +VerilogNetlistWriter::parameterWidthValue(const NetlistPort& port) { + return port.parentBlock().parameter(port.widthFormula()).value(); +} + + } diff --git a/openasip/src/applibs/ProGe/VerilogNetlistWriter.hh b/openasip/src/applibs/ProGe/VerilogNetlistWriter.hh index 40ae70e0bb..37334752ad 100644 --- a/openasip/src/applibs/ProGe/VerilogNetlistWriter.hh +++ b/openasip/src/applibs/ProGe/VerilogNetlistWriter.hh @@ -92,6 +92,7 @@ private: void writePortMappings( const BaseNetlistBlock& block, std::ofstream& stream) const; + static bool usesParameterWidth(const NetlistPort& port); std::string indentation(unsigned int level) const; /** @@ -112,6 +113,7 @@ private: static bool isNumber(const std::string& formula); static std::string portSignalName(const NetlistPort& port); static std::string portSignalType(const NetlistPort& port); + static TCEString parameterWidthValue(const NetlistPort& port); /// Width of the ground signal. int groundWidth_; diff --git a/openasip/src/applibs/TestGenerator/FUOperationTestGenerator.cc b/openasip/src/applibs/TestGenerator/FUOperationTestGenerator.cc new file mode 100644 index 0000000000..abb3091265 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/FUOperationTestGenerator.cc @@ -0,0 +1,243 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file OperationTestGenerator.cc + * + * Implementation of OperationTestGenerator class. + * + * Created on: 12.3.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "FUOperationTestGenerator.hh" + +#include +#include +#include +#include +#include + +#include "TestCase.hh" +#include "Program.hh" +#include "ADFSerializer.hh" +#include "Machine.hh" +#include "FunctionUnit.hh" +#include "CompileTools.hh" +#include "TCEString.hh" +#include "OperationPool.hh" +#include "Operation.hh" +#include "Operand.hh" +#include "HWOperation.hh" +#include "Conversion.hh" +#include "RandomNumberGenerator.hh" +#include "LLVMIRTools.hh" +#include "MachineInfo.hh" +#include "MathTools.hh" + +//#define DEBUG_OPERATIONTESTGEN +#ifdef DEBUG_OPERATIONTESTGEN +#include +#endif + +FUOperationTestGenerator::FUOperationTestGenerator() + : TestGeneratorBase( + std::string("operation-tests"), + std::string("Generates tests for basic operations for each Function " + "Unit. Operations to be testes are ones without side " + "effects and memory accesses.")) { +} + +FUOperationTestGenerator::~FUOperationTestGenerator() { +} + + +/** + * Generates test cases to try each pure operations of each function unit. + * + */ +std::vector +FUOperationTestGenerator::generateTestCasesImpl( + RandomNumberGenerator::SeedType seed) { + using namespace TTAMachine; + using namespace TTAProgram; + using std::endl; + std::vector testcases; + if (!Application::shellCommandsExists("tcecc")) { + return testcases; + } + + std::stringstream globalStream; + std::stringstream codeStream; + OperationPool operPool; + + RandomNumberGenerator throwDice(seed); + LLVMIRTools llvmir; + + typedef std::vector TestVectorListType; + typedef std::map + OperationTestVectorsMapType; + OperationTestVectorsMapType operationTestVectors; + std::map definedGlobals; + + // Generate separate test case for each function unit // + Machine::FunctionUnitNavigator fuNav = machine().functionUnitNavigator(); + for (int fui = 0; fui < fuNav.count(); fui++) { + const FunctionUnit& fu = *fuNav.item(fui); + operationTestVectors.clear(); + definedGlobals.clear(); + globalStream.str(std::string()); + codeStream.str(std::string()); + llvmir.resetIdentifiers(); + // Collect test vector for each operation of the function unit // + for (int opi = 0 ; opi < fu.operationCount(); opi++) { + const HWOperation& hwOperation = *fu.operation(opi); + const Operation& osalOperation = + operPool.operation(hwOperation.name().c_str()); + if (!osalOperation.isPure() + || osalOperation.numberOfOutputs() == 0) { + continue; + } + TestVectorListType& tmp = operationTestVectors[&hwOperation]; + osalOperation.makeTestVectors(tmp, throwDice()); + } + if (operationTestVectors.empty()) { + continue; + } + // Write LLVM IR assembly code from the test vectors // + OperationTestVectorsMapType::const_iterator otv_it; + + for (otv_it = operationTestVectors.begin(); + otv_it != operationTestVectors.end(); otv_it++) { + const HWOperation& hwOperation = *otv_it->first; + const TestVectorListType& testVectors = otv_it->second; + TestVectorListType::const_iterator tv_it; + for (tv_it = testVectors.begin(); + tv_it != testVectors.end(); tv_it++) { + const Operation::InputOperandVector& operandValues = *tv_it; + const Operation& osalOperation = + operPool.operation(hwOperation.name().c_str()); + std::string fuName = hwOperation.parentUnit()->name(); + std::string opName = hwOperation.name(); + std::string identifier = llvmir.newIdentifier(); + + LLVMIRTools::Type returnType = + llvmir.outputTypeListOf(osalOperation); + + // Call tce macro // + codeStream << " " << + llvmir.assignment( + identifier, + llvmir.inlineAssembly( + fuName + "." + TCEString::toUpper(opName), + returnType.toString(), + llvmir.inlineAssemblyArguments( + osalOperation, operandValues), + "" // = no meta data + ) + ) << endl; + // Store result to global variable so the results appears // + // on bus trace // + if (returnType.isStructure()) { + // Generate extract field instructions + for (size_t i = 0; i < returnType.subTypes().size(); i++) { + const LLVMIRTools::Type& subType = + returnType.subTypes().at(i); + std::string extractionIdentifier = + llvmir.newIdentifier(); + codeStream << " " << llvmir.extractValue( + extractionIdentifier, identifier, returnType, i) + << endl; + if (definedGlobals.count(subType.toString()) == 0) { + globalStream << llvmir.newGlobalDecl( + subType.toString() + " zeroinitializer", + Conversion::toString(subType.getAlignment())); + globalStream << endl; + definedGlobals[subType.toString()] = + llvmir.lastGlobalIdentifier(); + } + codeStream << " " << llvmir.store( + subType, extractionIdentifier, + definedGlobals[subType.toString()],true) << endl; + } + } else { + if (definedGlobals.count(returnType.toString()) == 0) { + globalStream << llvmir.newGlobalDecl( + returnType.toString() + " zeroinitializer", + Conversion::toString(returnType.getAlignment())); + globalStream << endl; + definedGlobals[returnType.toString()] = + llvmir.lastGlobalIdentifier(); + } + std::string typeStr = returnType.toString(); + codeStream << " " + << llvmir.store(returnType, identifier, + definedGlobals[typeStr], true) << endl; + } + } // for each operation test vectors + } // for each test vectors set of a operation + + if (codeStream.rdbuf()->in_avail() == 0) { + continue; + } + + // Finalize code and try to compile it and convert it to Program // + std::stringstream completeCode; + completeCode << llvmir.targetInfo(machine().isLittleEndian()) + << globalStream.rdbuf() + << llvmir.mainBody(codeStream.str()) << std::flush; + +#ifdef DEBUG_OPERATIONTESTGEN + std::string codeDumpStr = completeCode.str(); +#endif + + // Sequential schedule, so that operations are not optimized away. + // Compiling some emulation libs seems to take forever to compile. + std::string compileOptions = "--sequential-schedule --no-emulationlib"; + Program* compiled = + CompileTools::compileAsLLVM(machine(), completeCode, + compileOptions); + if (compiled != NULL) { + testcases.push_back(TestCase( + compiled, fu.name() + "-operation-tests")); + if (Application::spamVerbose()) { + std::cerr << title() <<": Generated test case for FU \"" + << fu.name() << "\"" << endl; + } + } else { + if (Application::spamVerbose()) { + std::cerr << title() <<": Compilation of a test case failed " + << "for FU " << fu.name() << endl; + } + } +#ifdef DEBUG_OPERATIONTESTGEN + std::ofstream codeDump(fu.name() + "-operation-tests.ll" + + TCEString::applyIf(compiled == NULL, ".fail")); + codeDump << codeDumpStr; + codeDump.close(); +#endif + } // End of for-each-FU + return testcases; +} + diff --git a/openasip/src/applibs/TestGenerator/FUOperationTestGenerator.hh b/openasip/src/applibs/TestGenerator/FUOperationTestGenerator.hh new file mode 100644 index 0000000000..f370f7e15a --- /dev/null +++ b/openasip/src/applibs/TestGenerator/FUOperationTestGenerator.hh @@ -0,0 +1,62 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file OperationTestGenerator.hh + * + * Declaration of OperationTestGenerator class. + * + * Created on: 12.3.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef OPERATIONTESTGENERATOR_HH +#define OPERATIONTESTGENERATOR_HH + +#include +#include +#include + +#include "Operation.hh" +#include "TestGeneratorBase.hh" + +namespace TTAMachine { + class HWOperation; +} + +/* + * Generates tests for simple operations of Function Units in the ADF. + */ +class FUOperationTestGenerator : public TestGeneratorBase { +public: + FUOperationTestGenerator(); + virtual ~FUOperationTestGenerator(); + +protected: + virtual std::vector generateTestCasesImpl( + RandomNumberGenerator::SeedType seed = + RandomNumberGenerator::DEFAULTSEED); +}; + +#endif /* OPERATIONTESTGENERATOR_HH */ diff --git a/openasip/src/applibs/TestGenerator/GuardTestGenerator.cc b/openasip/src/applibs/TestGenerator/GuardTestGenerator.cc new file mode 100644 index 0000000000..01891043d8 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/GuardTestGenerator.cc @@ -0,0 +1,235 @@ +/* + Copyright (c) 2002-2016 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. + */ +/* + * @file GuardTestGenerator.cc + * + * Implementation/Declaration of GuardTestGenerator class. + * + * Created on: 5.8.2016 + * @author Henry Linjamäki 2016 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "GuardTestGenerator.hh" + +#include +#include +#include + +#include "SnippetGenerator.hh" + +#include "ControlUnit.hh" +#include "Bus.hh" +#include "AddressSpace.hh" +#include "Guard.hh" + +#include "Program.hh" +#include "Procedure.hh" +#include "Move.hh" +#include "TerminalRegister.hh" +#include "TerminalImmediate.hh" +#include "MoveGuard.hh" + +#include "MachineConnectivityCheck.hh" + +#include "TCEString.hh" + +GuardTestGenerator::GuardTestGenerator() + : TestGeneratorBase("guard-tests", "Generates tests for move guards.") { + +} + +GuardTestGenerator::~GuardTestGenerator() { +} + +std::vector +GuardTestGenerator::generateTestCasesImpl( + RandomNumberGenerator::SeedType seed) { + + using namespace TTAMachine; + using namespace TTAProgram; + + RandomNumberGenerator rng(seed + 129594); + + auto& defaultCodeAS = *machine().controlUnit()->addressSpace(); + std::vector result; + + // Generate separate test case for each bus having RF guards // + for (auto bus : machine().busNavigator()) { + if (!hasRFGuards(*bus)) continue; + + std::unique_ptr tcProg(new Program(defaultCodeAS)); + Procedure* tcProc(new Procedure("guardtests", defaultCodeAS)); + tcProg->addProcedure(tcProc); + + std::unique_ptr tcCode(getGuardTests(*bus, rng())); + if (tcCode) tcProc->append(tcCode.release()); + + if (tcProc->instructionCount() == 0) continue; + + result.push_back(TestCase( + tcProg.release(), TCEString("rfguard-test-on-") + bus->name())); + } + + // TODO: Generate separate test case for each bus having fu port guards // + + return result; +} + +bool +GuardTestGenerator::hasRFGuards(const TTAMachine::Bus& bus) { + using TTAMachine::RegisterGuard; + for (int i = 0; i < bus.guardCount(); i++) { + auto* rfGuard = dynamic_cast(bus.guard(i)); + if (rfGuard) return true; + } + return false; +} + +std::set +GuardTestGenerator::registerGuards( + const TTAMachine::Bus& bus) { + using TTAMachine::RegisterGuard; + std::set result; + for (int i = 0; i < bus.guardCount(); i++) { + auto* rfGuard = dynamic_cast(bus.guard(i)); + if (rfGuard) result.insert(rfGuard); + } + return result; +} + +TTAProgram::CodeSnippet* +GuardTestGenerator::getGuardTests( + const TTAMachine::Bus& bus, + RandomNumberGenerator::SeedType seed) { + + using TTAProgram::CodeSnippet; + + std::unique_ptr tests(new CodeSnippet()); + + unsigned globalGuardLatency = + bus.machine()->controlUnit()->globalGuardLatency(); + + for (auto rfGuard : registerGuards(bus)) { + for (int64_t guardState : generateGuardStates(*rfGuard, seed) ) { + std::unique_ptr guardStateSet( + getSetGuardCode(*rfGuard, guardState)); + + // Assuming that guard value is loaded in the last instruction in + // guardStateSet. + unsigned guardEvaluationCycle = std::max( + globalGuardLatency + + (unsigned)rfGuard->registerFile()->guardLatency(), + 0u); + + std::unique_ptr moveWithGuard( + getMovementForGuard(*rfGuard, guardEvaluationCycle, false, + seed)); + + if (guardStateSet && moveWithGuard) { + tests->append(guardStateSet.release()); + tests->append(moveWithGuard.release()); + } + } + } + + return tests.release(); +} + +std::vector +GuardTestGenerator::generateGuardStates( + const TTAMachine::RegisterGuard& rfGuard, + RandomNumberGenerator::SeedType seed) { + + RandomNumberGenerator rng(seed + 11); + int regWidth = rfGuard.registerFile()->width(); + + assert(regWidth > 0); + if (regWidth == 1) { + return { 1, 0 }; + } else { + return { + 1, 0, + rng() % regWidth, rng() % regWidth, + rng() % regWidth, rng() % regWidth + }; + } + + return {}; +} + +/** + * Creates code snippet that loads the given value into the register specified + * by the register guard. + */ +TTAProgram::CodeSnippet* +GuardTestGenerator::getSetGuardCode( + const TTAMachine::RegisterGuard& rfGuard, int64_t value) { + + using namespace TTAProgram; + + std::unique_ptr code(SnippetGenerator::getValue(value, + *rfGuard.registerFile(), rfGuard.registerIndex())); + + return code.release(); +} + +/** + * Creates code snippet that has some bus movement with the given guard. + * + */ +TTAProgram::CodeSnippet* +GuardTestGenerator::getMovementForGuard( + TTAMachine::RegisterGuard& rfGuard, unsigned guardEvalCycle, + bool /*addNoiseMoves*/, RandomNumberGenerator::SeedType seed) { + + using namespace TTAProgram; + + RandomNumberGenerator rng(seed+92); + std::unique_ptr tests(new CodeSnippet()); + + + for (unsigned i = 1; i < guardEvalCycle; i++) { + auto nop = SnippetGenerator::getNOP( + *rfGuard.registerFile()->machine()); + if (!nop) return nullptr; + tests->append(nop); + } + + // Generate guarded movement on the bus at the guard evaluation // + auto bus = rfGuard.parentBus(); + if (bus->immediateWidth() < 0) return nullptr; + + Terminal* anyTerm = SnippetGenerator::getAnyWritablePort(*bus); + if (!anyTerm) return nullptr; + + auto terminalImm = new TerminalImmediate(SimValue(1, 1)); + auto guardedMove = SnippetGenerator::getInstruction( + new Move(terminalImm, anyTerm, *bus, new MoveGuard(rfGuard))); + if (!guardedMove) return nullptr; + + tests->add(guardedMove); + + return tests.release(); +} diff --git a/openasip/src/applibs/TestGenerator/GuardTestGenerator.hh b/openasip/src/applibs/TestGenerator/GuardTestGenerator.hh new file mode 100644 index 0000000000..8beaae5bda --- /dev/null +++ b/openasip/src/applibs/TestGenerator/GuardTestGenerator.hh @@ -0,0 +1,85 @@ +/* + Copyright (c) 2002-2016 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. + */ +/* + * @file GuardTestGenerator.hh + * + * Declaration of GuardTestGenerator class. + * + * Created on: 5.8.2016 + * @author Henry Linjamäki 2016 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef GUARDTESTGENERATOR_HH +#define GUARDTESTGENERATOR_HH + +#include "TestGeneratorBase.hh" + +#include +#include + +namespace TTAMachine { + class Bus; + class RegisterGuard; +} + +namespace TTAProgram { + class CodeSnippet; +} + +/* + * Generates tests for guards. + */ +class GuardTestGenerator : public TestGeneratorBase { +public: + GuardTestGenerator(); + virtual ~GuardTestGenerator(); + +protected: + virtual std::vector generateTestCasesImpl( + RandomNumberGenerator::SeedType seed = + RandomNumberGenerator::DEFAULTSEED) override; + +private: + + static bool hasRFGuards(const TTAMachine::Bus& bus); + static std::set registerGuards( + const TTAMachine::Bus& bus); + static TTAProgram::CodeSnippet* getGuardTests( + const TTAMachine::Bus& bus, + RandomNumberGenerator::SeedType seed); + static std::vector generateGuardStates( + const TTAMachine::RegisterGuard& rfGuard, + RandomNumberGenerator::SeedType seed); + static TTAProgram::CodeSnippet* getSetGuardCode( + const TTAMachine::RegisterGuard& rfGuard, + int64_t value); + static TTAProgram::CodeSnippet* getMovementForGuard( + TTAMachine::RegisterGuard& rfGuard, + unsigned guardEvalCycle, + bool addNoiseMoves, + RandomNumberGenerator::SeedType seed); +}; + +#endif /* GUARDTESTGENERATOR_HH */ diff --git a/openasip/src/applibs/TestGenerator/ICacheTestGenerator.cc b/openasip/src/applibs/TestGenerator/ICacheTestGenerator.cc new file mode 100644 index 0000000000..52426fa122 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/ICacheTestGenerator.cc @@ -0,0 +1,488 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file ICacheTestGenerator.cc + * + * Implementation of ICacheTestGenerator class. + * + * Created on: 21.9.2015 + * @author Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "ICacheTestGenerator.hh" + +#include +#include +#include +#include + +#include "Machine.hh" +#include "MachineConnectivityCheck.hh" +#include "MachineInfo.hh" +#include "Move.hh" +#include "Bus.hh" +#include "ControlUnit.hh" +#include "Terminal.hh" +#include "TerminalRegister.hh" +#include "TerminalFUPort.hh" +#include "TerminalImmediate.hh" +#include "Instruction.hh" +#include "Procedure.hh" +#include "Program.hh" +#include "FUPort.hh" +#include "Immediate.hh" + +#include "Application.hh" +#include "MathTools.hh" + +#include "MachineImplementation.hh" +#include "CacheImplementation.hh" + +//#define DEBUG_ICACHETESTGEN + +static const std::vector emptyTestSet = std::vector(); + + +ICacheTestGenerator::ICacheTestGenerator() + : TestGeneratorBase( + "icache-test-generator", + "Creates a test program that stresses instruction cache(s) if.", + true) { + +} + +ICacheTestGenerator::~ICacheTestGenerator() { +} + +std::vector +ICacheTestGenerator::generateTestCasesImpl( + RandomNumberGenerator::SeedType seed) { + + using namespace TTAMachine; + using namespace TTAProgram; + + if (!initializedWithIdf() || !initializedWithAdf()) { + if (Application::spamVerbose()) { + std::cerr << title() << ": Missing ADF and/or IDF. Skipping." + << std::endl; + } + return emptyTestSet; + } + if (!implementation().hasL1InstructionCache()) { + if (Application::spamVerbose()) { + std::cerr << title() + << ": IDF lacks instruction cache. Skipping." + << std::endl; + } + return emptyTestSet; + } + + seed_ = seed; + std::unique_ptr prog(new Program( + *machine().controlUnit()->addressSpace())); + + generateTestProgram(*prog); + if (prog->procedureCount() == 0) { + return emptyTestSet; + } + + //todo simulate reference data for cache statistics? + + return std::vector{TestCase(prog.release(), "icache-test")}; +} + +/** + * Generates the test program for instruction cache. + */ +void +ICacheTestGenerator::generateTestProgram(TTAProgram::Program& prog) { + using namespace TTAMachine; + using namespace TTAProgram; + + /* + * Generates jumps to random locations in subspace of instruction address + * space that should cover L1 instruction cache. That is, the cache would + * get fully filled and have some its blocks evicted. + * + * The subspace is divided into jump block - that consists of jump + * instruction and delay slots (NOPs) - and they are chained together. + */ + + if (!machine().controlUnit()->hasOperation("jump")) { + if (Application::spamVerbose()) { + std::cerr << title() + << ": TTA does not have jump capability. Skipping." + << std::endl; + } + return; + } + + const IDF::CacheImplementation& l1 = implementation().l1InstructionCache(); + int cacheSize = l1.blockSize() * l1.setSize() * l1.cacheSize(); + const InstructionAddress startAddr = + machine().controlUnit()->addressSpace()->start(); + // Set limit to point where instruction cache should get filled and have + // its blocks be evicted but not past machines usable address space. + InstructionAddress upperLimitAddr = + std::min(startAddr + static_cast(cacheSize*3), + machine().controlUnit()->addressSpace()->end()); + const unsigned delaySlots = + static_cast(machine().controlUnit()->delaySlots()); + FUPort& jumpPort = + *dynamic_cast(machine().controlUnit()->triggerPort()); + + LIMMSource limmSource = selectLIMMSource(jumpPort); + bool useLimm = false; + Bus* jumpAddressFromSimmBus = selectSIMMSource(jumpPort); + + // If SIMM cannot use (lacking range for upper addresses) check if LIMM + // can be used instead for jump addresses. + unsigned maxJumpAddress = 0; + if (jumpAddressFromSimmBus == nullptr || + (jumpAddressFromSimmBus->immediateWidth() < + MathTools::requiredBits(upperLimitAddr))) { + + if (std::get<0>(limmSource) >= static_cast( + MathTools::requiredBits(upperLimitAddr))) { + useLimm = true; + if (std::get<0>(limmSource) > 31) { + maxJumpAddress = std::numeric_limits::max(); + } else { + maxJumpAddress = (1 << std::get<0>(limmSource)) - 1; + } + } else { + if (Application::spamVerbose()) { + std::cerr << title() + << ": Cannot generate random jumps. Skipping." + << std::endl; + } + return; + } + } else { + unsigned simmWidth = jumpAddressFromSimmBus->immediateWidth(); +#ifdef DEBUG_ICACHETESTGEN + std::cerr << "simmWidth = " << simmWidth << std::endl; +#endif + if (simmWidth > 31) { + maxJumpAddress = std::numeric_limits::max(); + } else { + maxJumpAddress = (1 << simmWidth) - 1; + } + } + +#ifdef DEBUG_ICACHETESTGEN + if (useLimm && std::get<0>(limmSource) > 0) { + std::cerr << "bits: " << std::get<0>(limmSource) << std::endl; + std::cerr << " bus: " << std::get<1>(limmSource)->name() << std::endl; + std::cerr << " IU: " << std::get<2>(limmSource)->name() << std::endl; + std::cerr << " IT: " << std::get<3>(limmSource)->name() << std::endl; + } +#endif + + maxJumpAddress = std::min(maxJumpAddress, upperLimitAddr); + std::unique_ptr jumpProc(new Procedure( + "randomjumper", *machine().controlUnit()->addressSpace())); + +#ifdef DEBUG_ICACHETESTGEN + std::cerr << "maxJumpAddress = " << maxJumpAddress << std::endl; +#endif + + std::vector jumpChain; + unsigned jumpBlockSize = (useLimm)?(2+delaySlots):(1+delaySlots); + generateJumpTargets(jumpChain, startAddr, maxJumpAddress, jumpBlockSize); + +#ifdef DEBUG_ICACHETESTGEN + std::cerr << "Jumps:"; +#endif + std::map jumpFromToTable; + InstructionAddress currentAddr = startAddr; + for (InstructionAddress next : jumpChain) { + jumpFromToTable.insert({currentAddr, next}); +#ifdef DEBUG_ICACHETESTGEN + std::cerr << "[" << currentAddr << " -> " << next << "] "; +#endif + currentAddr = next; + } +#ifdef DEBUG_ICACHETESTGEN + std::cerr << std::endl; +#endif + + for (auto jumpFromTo : jumpFromToTable) { + if (useLimm) { + generateJumpWithLIMM(*jumpProc, *std::get<1>(limmSource), + jumpPort, jumpFromTo.second, limmSource); + } else { + generateJumpWithSIMM(*jumpProc, *jumpAddressFromSimmBus, + jumpPort, jumpFromTo.second); + } + } + if (jumpProc->instructionCount() > 0) { + prog.addProcedure(jumpProc.release()); + } +} + +/** + * Selects suitable SIMM source that can be used directly for target port + * (forPort). + * + * Selects greedily a SIMM source that holds a largest value. + * + * @param forPort The target port. + * @returns Suitable SIMM source bus connected to given port. Nullptr if could + * not found the suitable source. + */ +TTAMachine::Bus* +ICacheTestGenerator::selectSIMMSource(TTAMachine::FUPort& forPort) { + using namespace TTAMachine; + using namespace TTAProgram; + using MCC = MachineConnectivityCheck; + + Bus* selectedBus = nullptr; + unsigned maxUnsignedSimm = 0; + for (Bus* busCandidate : machine().busNavigator()) { + if (MCC::isConnected(forPort, *busCandidate)) { + if (busCandidate->immediateWidth() <= 0) { + continue; + } + unsigned simmWidth = busCandidate->immediateWidth(); + if (simmWidth > maxUnsignedSimm) { + maxUnsignedSimm = busCandidate->immediateWidth(); + selectedBus = busCandidate; + } + } + } + return selectedBus; +} + +/** + * Selects suitable LIMM source that can be used directly for target port + * (forPort). + * + * Selects greedily a LIMM source that holds a largest value. + * + * @param forPort The target port. + * @returns Returns tuple of (unsigned, Bus*, IU*, InstructionTemplate*). + * unsigned is for largest value to be carried to the target port. + * Bus pointer is the suitable way from a IU to target port. The IU + * pointer is for the suitable LIMM source. The InstructionTemplate + * pointer is the selected template to carry the LIMM. + */ +ICacheTestGenerator::LIMMSource +ICacheTestGenerator::selectLIMMSource(TTAMachine::FUPort& forPort) { + using namespace TTAMachine; + using namespace TTAProgram; + using MCC = MachineConnectivityCheck; + + // todo? should take account the port widths too. + + LIMMSource result{0, nullptr, nullptr, nullptr}; + if (machine().immediateUnitNavigator().count() == 0) { + return result; + } + + std::vector suitableIUs; + std::map suitableRoutes; + for (ImmediateUnit* iu : machine().immediateUnitNavigator()) { + if (iu->portCount() == 0) continue; + RFPort* iuPort = iu->port(0); + MCC::BusSet tmp = MCC::findRoutes(*iuPort, forPort); + suitableRoutes[iu].insert(tmp.begin(), tmp.end()); + suitableIUs.push_back(iu); + } + + for (ImmediateUnit* iu : suitableIUs) { + unsigned widestFoundBitWidthViaBus = 0; + Bus* selectedRoute = nullptr; + + for (Bus* route : suitableRoutes.at(iu)) { + unsigned routeWidth = static_cast(route->width()); + if (routeWidth > widestFoundBitWidthViaBus) { + widestFoundBitWidthViaBus = route->width(); + selectedRoute = route; + } + } + + unsigned int widestFoundBitWidthViaIT = 0; + InstructionTemplate* selectedIT = nullptr; + for (InstructionTemplate* it : + machine().instructionTemplateNavigator()) { + + unsigned widestWidthViaIT = it->supportedWidth(*iu); + // Seeking max unsigned value to be carried -> drop one bit if + // sign extended. + if (widestWidthViaIT > 0 && iu->signExtends()) { + widestWidthViaIT -= 1; + } + // Note: now convert bit width -> largest value holded. + if (widestWidthViaIT > widestFoundBitWidthViaIT) { + widestFoundBitWidthViaIT = widestWidthViaIT; + selectedIT = it; + } + } + + if (selectedRoute != nullptr && selectedIT != nullptr) { + unsigned widestBitWidthCarried = std::min( + widestFoundBitWidthViaBus, widestFoundBitWidthViaIT); + if (widestBitWidthCarried > std::get<0>(result)) { + assert(selectedRoute != nullptr); + assert(iu != nullptr); + assert(selectedIT != nullptr); + result = std::make_tuple( + widestBitWidthCarried, + selectedRoute, iu, selectedIT); + } + } + } + + return result; +} + +/** + * Generates random jump targets ahead of jumpTargets and at maximum distance + * of maxJumpHeight. + * + * The result list consists of addresses to jump in order: + * addr0 -> addr1 -> addr2 -> ... -> addrn. + * Last item in jumpTargets is the largest address; others are randomized. + */ +void +ICacheTestGenerator::generateJumpTargets( + std::vector& jumpTargets, + InstructionAddress startJumpLoc, + unsigned maxJumpHeight, + unsigned jumpBlockSize) { + + jumpTargets.clear(); + if (maxJumpHeight < jumpBlockSize) { + return; + } + + // jump instruction + delay slots + unsigned numTargets = maxJumpHeight/jumpBlockSize; + + jumpTargets.reserve(numTargets); + for (unsigned i = jumpBlockSize; i <= (maxJumpHeight - jumpBlockSize); ) { + jumpTargets.push_back(startJumpLoc + i); + i += jumpBlockSize; + } + if (jumpTargets.empty()) { + return; + } + + RandomNumberGenerator rng(seed_); + std::random_shuffle(jumpTargets.begin(), --jumpTargets.end(), + [&rng] (unsigned n) -> unsigned { + return rng() % n; + }); + seed_ = rng(); +} + +/** + * Generates jump instruction (with delay slots) to given location and adds + * it to procedure. + */ +void +ICacheTestGenerator::generateJumpWithSIMM( + TTAProgram::Procedure& targetProc, + TTAMachine::Bus& bus, + const TTAMachine::FUPort& jumpPort, + InstructionAddress jumpTarget) { + + using namespace TTAMachine; + using namespace TTAProgram; + + //todo? select suitable instruction template. + + assert(jumpPort.isOpcodeSetting()); + const TTAMachine::HWOperation& jumpOper = + *jumpPort.parentUnit()->operation("jump"); + + Instruction* jumpInst = new Instruction(); + jumpInst->addMove(std::make_shared( + new TerminalImmediate(SimValue(jumpTarget, bus.width())), + new TerminalFUPort(jumpPort, jumpOper), + bus)); + targetProc.add(jumpInst); + // Add NOP instruction into delay slots. + // todo: May be add some dummy moves instead of NOPs for bus and cache + // activity? + for (int i = 0; i < machine().controlUnit()->delaySlots(); i++) { + targetProc.add(new Instruction()); + } +} + +void +ICacheTestGenerator::generateJumpWithLIMM( + TTAProgram::Procedure& targetProc, + TTAMachine::Bus& bus, + const TTAMachine::FUPort& jumpPort, + InstructionAddress jumpTarget, + ICacheTestGenerator::LIMMSource& limmSource) { + + using namespace TTAMachine; + using namespace TTAProgram; + + + unsigned limmWidth = std::get<0>(limmSource); + ImmediateUnit* iu = std::get<2>(limmSource); + InstructionTemplate* it = std::get<3>(limmSource); + + Instruction* limmInst = new Instruction(*it); + Immediate* jumpAddr = new Immediate( + new TerminalImmediate(SimValue(jumpTarget, limmWidth)), + new TerminalRegister(*iu->port(0), 0)); + limmInst->addImmediate(std::shared_ptr(jumpAddr)); + + assert(jumpPort.isOpcodeSetting()); + const TTAMachine::HWOperation& jumpOper = + *jumpPort.parentUnit()->operation("jump"); + + Instruction* jumpInst = new Instruction(); + jumpInst->addMove(std::make_shared( + new TerminalRegister(*iu->port(0), 0), + new TerminalFUPort(jumpPort, jumpOper), + bus)); + + targetProc.add(limmInst); + targetProc.add(jumpInst); + + // Add NOP instruction into delay slots. + // todo: May be add some dummy moves instead of NOPs for bus and cache + // activity? + for (int i = 0; i < machine().controlUnit()->delaySlots(); i++) { + targetProc.add(new Instruction()); + } +} + + +void +ICacheTestGenerator::generateDummyInstructions( + TTAProgram::Procedure& /*targetProc*/, + unsigned int /*numOfInstructions*/) { + + // use limm templates + // write simms +} + diff --git a/openasip/src/applibs/TestGenerator/ICacheTestGenerator.hh b/openasip/src/applibs/TestGenerator/ICacheTestGenerator.hh new file mode 100644 index 0000000000..676a7362ce --- /dev/null +++ b/openasip/src/applibs/TestGenerator/ICacheTestGenerator.hh @@ -0,0 +1,104 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file ICacheTestGenerator.hh + * + * Declaration of ICacheTestGenerator class. + * + * Created on: 21.9.2015 + * @author Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef ICACHETESTGENERATOR_HH +#define ICACHETESTGENERATOR_HH + +#include +#include + +#include "TestGeneratorBase.hh" + +#include "BaseType.hh" + +namespace TTAProgram { + class Program; + class Procedure; +} + +namespace TTAMachine { + class Bus; + class FUPort; + class ImmediateUnit; + class InstructionTemplate; +} + +/* + * Generates test program to stress instruction caches. + */ +class ICacheTestGenerator : public TestGeneratorBase { +public: + ICacheTestGenerator(); + virtual ~ICacheTestGenerator(); + +private: + virtual std::vector generateTestCasesImpl( + RandomNumberGenerator::SeedType seed = + RandomNumberGenerator::DEFAULTSEED) override; + using LIMMSource = std::tuple< + unsigned, /* The widest unsigned value in bits that can be carried. */ + TTAMachine::Bus*, + TTAMachine::ImmediateUnit*, + TTAMachine::InstructionTemplate*>; + + void generateTestProgram(TTAProgram::Program& prog); + TTAMachine::Bus* selectSIMMSource(TTAMachine::FUPort& forPort); + LIMMSource selectLIMMSource(TTAMachine::FUPort& forPort); + + void generateJumpTargets( + std::vector& jumpTargets, + InstructionAddress startJumpLoc, + unsigned maxJumpHeight, + unsigned jumpBlockSize); + + void generateJumpWithSIMM( + TTAProgram::Procedure& targetProc, + TTAMachine::Bus& bus, + const TTAMachine::FUPort& jumpPort, + InstructionAddress jumpTarget); + void generateJumpWithLIMM( + TTAProgram::Procedure& targetProc, + TTAMachine::Bus& bus, + const TTAMachine::FUPort& jumpPort, + InstructionAddress jumpTarget, + LIMMSource& limmSource); + static void generateDummyInstructions( + TTAProgram::Procedure& targetProc, + unsigned int numOfInstructions = 1); + + RandomNumberGenerator::SeedType seed_ = RandomNumberGenerator::DEFAULTSEED; + +}; + + +#endif /* ICACHETESTGENERATOR_HH */ diff --git a/openasip/src/applibs/TestGenerator/ImmediateTestGenerator.cc b/openasip/src/applibs/TestGenerator/ImmediateTestGenerator.cc new file mode 100644 index 0000000000..ae205d1be8 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/ImmediateTestGenerator.cc @@ -0,0 +1,286 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file ImmediateTestGenerator.cc + * + * Implementation of ImmediateTestGenerator class. + * + * Created on: 9.3.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam.tut.fi) + * @note rating: red + */ + +#include "ImmediateTestGenerator.hh" + +#include +#include + +#include "Machine.hh" +#include "Move.hh" +#include "Bus.hh" +#include "ControlUnit.hh" +#include "Terminal.hh" +#include "TerminalRegister.hh" +#include "TerminalFUPort.hh" +#include "TerminalImmediate.hh" +#include "Program.hh" +#include "Instruction.hh" +#include "Procedure.hh" +#include "MachineConnectivityCheck.hh" +#include "MathTools.hh" +#include "TemplateSlot.hh" +#include "LimmTestSnippetGenerator.hh" + + +ImmediateTestGenerator::ImmediateTestGenerator() + : TestGeneratorBase( + std::string("immediate-tests"), + std::string( + "Generates various short and long immediate values. ")) { +} + +ImmediateTestGenerator::~ImmediateTestGenerator() { +} + + +std::vector +ImmediateTestGenerator::generateTestCasesImpl( + RandomNumberGenerator::SeedType seed) { + using namespace TTAMachine; + using namespace TTAProgram; + + std::vector testcases; + Program* prog = new Program(*machine().controlUnit()->addressSpace()); + + // Generate SIMMs // + Procedure* simmproc = + new Procedure("testsimms", *machine().controlUnit()->addressSpace()); + + const Machine::BusNavigator& busNav = machine().busNavigator(); + for (int i = 0; i < busNav.count(); i++) { + Bus& bus = *busNav.item(i); + Terminal* dstTerminal = findAnyWritableTerminal( + bus, bus.immediateWidth()); + if (dstTerminal == nullptr) { + continue; + } + generateSIMMMoves(*simmproc, bus, *dstTerminal, seed); + } + if (simmproc->instructionCount() == 0) { + delete simmproc; + } else { + prog->addProcedure(simmproc); + testcases.push_back(TestCase(prog, "simm-tests")); + } + + // Generate LIMMs // + prog = new Program(*machine().controlUnit()->addressSpace()); + Procedure* limmproc = + new Procedure("testlimms", *machine().controlUnit()->addressSpace()); + + for (auto it : machine().instructionTemplateNavigator()) { + if (!targetsAnyIU(*it)) { + continue; + } + generateLIMMTestCode(*it, *limmproc, seed); + } + + if (simmproc->instructionCount() == 0) { + delete limmproc; + } else { + prog->addProcedure(limmproc); + testcases.push_back(TestCase(prog, "limm-tests")); + } + + return testcases; +} + +/** + * Seeks any unit port to write an immediate and returns Terminal of it. + * + * Otherwise returns NULL. + */ +TTAProgram::Terminal* +ImmediateTestGenerator::findAnyWritableTerminal( + const TTAMachine::Bus& bus, + unsigned transportBitWidth) { + using namespace TTAMachine; + using namespace TTAProgram; + + const Machine& mach = *bus.machine(); + const RegisterFile* candidateRF = nullptr; + int candidateRFWidth = 0; + + // Seek writable port among RFs // + const Machine::RegisterFileNavigator rfNav = mach.registerFileNavigator(); + for (int i = 0; i < rfNav.count(); i++) { + const RegisterFile& rf = *rfNav.item(i); + if (rf.maxWrites() < 1 || rf.size() < 1) + continue; + if (!MachineConnectivityCheck::busConnectedToRF(bus, rf)) + continue; + + if (rf.width() > candidateRFWidth) { + candidateRF = &rf; + candidateRFWidth = rf.width(); + } + if (rf.width() >= static_cast(transportBitWidth)) { + break; + } + } + + if (candidateRF) { + return new TerminalRegister(*candidateRF->firstReadPort(), 0); + } + + // Seek writable port among non-triggering FU ports // + // todo + + return nullptr; +} + + +TTAProgram::Terminal* +ImmediateTestGenerator::makeSimmTerminal(const TTAMachine::Bus& bus, + int value) { + + return new TTAProgram::TerminalImmediate( + SimValue(value, bus.immediateWidth())); +} + + +/** + * Generates test immediate instructions for the given bus. + * + * The instructions are appended to the procedure. + */ +void +ImmediateTestGenerator::generateSIMMMoves( + TTAProgram::Procedure& targetProcedure, + TTAMachine::Bus& targetBus, + const TTAProgram::Terminal& targetTerminal, + RandomNumberGenerator::SeedType seed) { + + using namespace TTAProgram; + RandomNumberGenerator dice(seed); + + // Prevents transporting values that are wider than anything between + // immediate and target terminal. + unsigned transportWidth = static_cast(std::min({ + targetBus.immediateWidth(), + targetTerminal.port().width(), + 31 })); + const int transportMask = static_cast(~(~(0ul) << transportWidth)); + + targetProcedure.add(makeSIMMInstruction(targetBus, targetTerminal, 1)); + targetProcedure.add(makeSIMMInstruction(targetBus, targetTerminal, 0)); + targetProcedure.add(makeSIMMInstruction( + targetBus, targetTerminal, -1 % transportMask)); + targetProcedure.add(makeSIMMInstruction( + targetBus, targetTerminal, 0)); + for (int i = 0; i < 100; i++) { + targetProcedure.add(makeSIMMInstruction( + targetBus, targetTerminal, dice() % transportMask)); + } + targetProcedure.add(makeSIMMInstruction(targetBus, targetTerminal, 0)); +} + +/** + * Creates instruction that transport short immediate to targeted terminal. + */ +TTAProgram::Instruction* +ImmediateTestGenerator::makeSIMMInstruction( + TTAMachine::Bus& targetBus, + const TTAProgram::Terminal& targetTerminal, + int value) { + + using namespace TTAMachine; + using namespace TTAProgram; + + Instruction* inst = new Instruction(); + inst->addMove(std::make_shared( + makeSimmTerminal(targetBus, value), targetTerminal.copy(), targetBus)); + return inst; +} + +/** + * Returns true, if the IT writes to any IU. + */ +bool +ImmediateTestGenerator::targetsAnyIU( + const TTAMachine::InstructionTemplate& it) { + for (int i = 0; i < it.slotCount(); i++) { + if (it.slot(i)->destination() != nullptr && it.slot(i)->width() > 0) { + return true; + } + } + return false; +} + +/** + * Generates Long immediate test code. + * + * The instruction template is used to load various values to IUs specified in + * it. Generated code is appended to the Procedure object. + * + */ +void +ImmediateTestGenerator::generateLIMMTestCode( + const TTAMachine::InstructionTemplate& it, + TTAProgram::Procedure& targetProcedure, + RandomNumberGenerator::SeedType seed) { + + if (!targetsAnyIU(it)) { + return; + } + + LimmTestSnippetGenerator limmTestGen(it); + if (!limmTestGen.valid()) { + // Can not generate test code for some reason. + return; + } + + // Generate basic LIMMs // + for (int64_t val : { 0, 1, 0, -1, 0 } ) { + for (int i = 0; i < limmTestGen.inputCount(); i++) { + int64_t mask = -1; + mask = ~(mask << limmTestGen.inputBitWidth(i)); + limmTestGen.assignInput(i, val & mask); + } + targetProcedure.append(limmTestGen.generateSnippet()); + } + + // Generate random LIMMs // + RandomNumberGenerator rng(seed); + for (int c = 0; c < 20; c++) { + for (int i = 0; i < limmTestGen.inputCount(); i++) { + int64_t mask = -1; + mask = ~(mask << limmTestGen.inputBitWidth(i)); + limmTestGen.assignInput(i, rng() & mask); + } + targetProcedure.append(limmTestGen.generateSnippet()); + } +} + + diff --git a/openasip/src/applibs/TestGenerator/ImmediateTestGenerator.hh b/openasip/src/applibs/TestGenerator/ImmediateTestGenerator.hh new file mode 100644 index 0000000000..b69511e7fb --- /dev/null +++ b/openasip/src/applibs/TestGenerator/ImmediateTestGenerator.hh @@ -0,0 +1,86 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file ImmediateTestGenerator.hh + * + * Declaration of ImmediateTestGenerator class. + * + * Created on: 9.3.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam.tut.fi) + * @note rating: red + */ + +#ifndef IMMEDIATETESTGENERATOR_HH +#define IMMEDIATETESTGENERATOR_HH + +#include "TestGeneratorBase.hh" +#include "RandomNumberGenerator.hh" + +namespace TTAProgram { + class Terminal; + class Procedure; + class Instruction; +} + +namespace TTAMachine { + class Bus; + class ImmediateUnit; + class InstructionTemplate; +} + +/* + * The test generator for generating short and long immediates. + */ +class ImmediateTestGenerator: public TestGeneratorBase { +public: + ImmediateTestGenerator(); + virtual ~ImmediateTestGenerator(); + +protected: + virtual std::vector generateTestCasesImpl( + RandomNumberGenerator::SeedType seed); + +private: + static TTAProgram::Terminal* findAnyWritableTerminal( + const TTAMachine::Bus& bus, + unsigned transportBitWidth); + static TTAProgram::Terminal* makeSimmTerminal( + const TTAMachine::Bus& bus, int value); + static void generateSIMMMoves( + TTAProgram::Procedure& targetProcedure, + TTAMachine::Bus& targetBus, + const TTAProgram::Terminal& targetTerminal, + RandomNumberGenerator::SeedType seed); + static TTAProgram::Instruction* makeSIMMInstruction( + TTAMachine::Bus& targetBus, + const TTAProgram::Terminal& targetTerminal, + int value); + static bool targetsAnyIU(const TTAMachine::InstructionTemplate& it); + static void generateLIMMTestCode( + const TTAMachine::InstructionTemplate& it, + TTAProgram::Procedure& targetProcedure, + RandomNumberGenerator::SeedType seed); +}; + +#endif /* IMMEDIATETESTGENERATOR_HH */ diff --git a/openasip/src/applibs/TestGenerator/LimmTestSnippetGenerator.cc b/openasip/src/applibs/TestGenerator/LimmTestSnippetGenerator.cc new file mode 100644 index 0000000000..380bdb5c48 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/LimmTestSnippetGenerator.cc @@ -0,0 +1,245 @@ +/* + Copyright (c) 2002-2016 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. + */ +/* + * @file LimmTestSnippetGenerator.cc + * + * Implementation of LimmTestSnippetGenerator class. + * + * Created on: 2.8.2016 + * @author Henry Linjamäki 2016 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "LimmTestSnippetGenerator.hh" + +#include +#include +#include + +#include "Machine.hh" +#include "InstructionTemplate.hh" +#include "TemplateSlot.hh" + +#include "CodeSnippet.hh" +#include "Instruction.hh" +#include "Move.hh" +#include "Immediate.hh" +#include "TerminalImmediate.hh" +#include "TerminalRegister.hh" + +#include "MachineConnectivityCheck.hh" + +#include "CodeSnippet.hh" +#include "Procedure.hh" + +#include "MathTools.hh" + + +LimmTestSnippetGenerator::LimmTestSnippetGenerator( + const TTAMachine::InstructionTemplate& it) : it_(it) { + + using TTAMachine::ImmediateUnit; + + std::set ius; + + // Gather unique IUs targeted by the IT. + for (int i = 0; i < it.slotCount(); i++) { + if (it.slot(i)->width() > 0) { + assert(it.slot(i)->destination()); + ius.insert(it.slot(i)->destination()); + } + } + + // Create test input points and update supported widths. + for (auto iu : ius) { + testInputs_.emplace_back(targetIU(it.supportedWidth(*iu), *iu)); + } + + // Values loaded into IUs are made visible for bus trace verification. That + // is, the loaded values are transported via some buses. + // Init the structs with a lambda function: + // (iu register index : int) -> Instruction*, + // where the returned instruction does the transport from a single IU. + for (targetIU& tiu : testInputs_) { + tiu.sinkFn = getSinkRoute(tiu.iu, tiu.bitWidth); + if (!tiu.sinkFn) return; + } + + valid_ = true; +} + +LimmTestSnippetGenerator::~LimmTestSnippetGenerator() { +} + +int +LimmTestSnippetGenerator::inputCount() const { + assert(valid_ && "Snippet template generator is not valid to use."); + return testInputs_.size(); +} + +int +LimmTestSnippetGenerator::inputBitWidth(int i) const { + assert(valid_ && "Snippet template generator is not valid to use."); + return testInputs_.at(i).bitWidth; +} + +/** + * Sets value to be loaded into some IU. + * + * The value must be requiredBits(val) <= inputBitWidth(i). + * + * At construction time, by default, input values are zeroes. + */ +void +LimmTestSnippetGenerator::assignInput(int i, uint64_t val) { + assert(valid_ && "Snippet template generator is not valid to use."); + assert(testInputs_.at(i).bitWidth > 0); + assert(MathTools::requiredBits(val) <= testInputs_.at(i).bitWidth); + testInputs_.at(i).testInput = val; +} + +/** + * Generates the snippet. + */ +TTAProgram::CodeSnippet* +LimmTestSnippetGenerator::generateSnippet() const { + assert(valid_ && "Snippet template generator is not valid to use."); + + TTAProgram::CodeSnippet* code = new TTAProgram::CodeSnippet(); + assert(!testInputs_.empty()); + code->add(makeLimmLoadInstruction(testInputs_, it_)); + for (const auto& i : testInputs_) { + code->add(i.sinkFn(0)); + } + + return code; //placeholder +} + +/** + * Used for creating lambda function that creates an instruction to do value + * transportation from an IU to anywhere. + */ +std::function makeSinkFn( + const TTAMachine::RFPort& src, + TTAMachine::Bus& bus, + const TTAMachine::Port& dst, + const TTAMachine::InstructionTemplate& it) { + + using namespace TTAProgram; + + std::shared_ptr instrTempl(new Instruction(it)); + auto srcTerm = new TerminalRegister(src, 0); + auto dstTerm = new TerminalRegister(dst, 0); + instrTempl->addMove(std::make_shared(srcTerm, dstTerm, bus)); + + auto result = [=](int) -> TTAProgram::Instruction* { + return instrTempl->copy(); + }; + + return result; +} + + +/** + * Creates function that generates an instruction to transport value from IU to + * anywhere. + * + * May return empty function. + */ +std::function +LimmTestSnippetGenerator::getSinkRoute( + const TTAMachine::ImmediateUnit& iu, int transportWidth) { + + using namespace TTAMachine; + using MCC = MachineConnectivityCheck; + + std::function result; // Empty function. + + const Machine& mach = *iu.machine(); + + std::vector sinkPorts; + for (auto rf : mach.registerFileNavigator()) { + if (rf->maxWrites() < 1 || rf->size() < 1) + continue; + const TTAMachine::Port* rfPort = rf->firstReadPort(); + if (rfPort->width() < transportWidth) continue; + sinkPorts.push_back(rfPort); + } + + std::vector> busRoutes; + for (auto bus : mach.busNavigator()) { + InstructionTemplate* itForMove = nullptr; + for (auto it : mach.instructionTemplateNavigator()) { + if (!it->usesSlot(bus->name()) + && it->numberOfDestinations() == 0) { + itForMove = it; + break; + } + } + + busRoutes.push_back(std::make_pair(bus, itForMove)); + } + + for(int srcIdx = 0; srcIdx < iu.portCount(); srcIdx++) { + auto srcPort = iu.port(srcIdx); + if (srcPort->width() < transportWidth) continue; + + for (auto sinkPort : sinkPorts) { + for (auto bus : busRoutes) { + if (MCC::isConnected(*srcPort, *bus.first) + && MCC::isConnected(*bus.first, *sinkPort)) { + return makeSinkFn( + *srcPort, *bus.first, *sinkPort, *bus.second); + } + } + } + } + + return result; +} + +/** + * Creates instruction that loads values into IUs using the instruction + * template. + */ +TTAProgram::Instruction* +LimmTestSnippetGenerator::makeLimmLoadInstruction( + const std::vector& inputValues, + const TTAMachine::InstructionTemplate& it) { + + using namespace TTAProgram; + + Instruction* instr = new Instruction(it); + + for (auto input : inputValues) { + auto termImm = new TerminalImmediate( + SimValue(input.testInput, input.bitWidth)); + auto iuTerm = new TerminalRegister(*input.iu.port(0), 0); + instr->addImmediate(std::make_shared(termImm, iuTerm)); + } + + return instr; +} + + diff --git a/openasip/src/applibs/TestGenerator/LimmTestSnippetGenerator.hh b/openasip/src/applibs/TestGenerator/LimmTestSnippetGenerator.hh new file mode 100644 index 0000000000..0cb41645ed --- /dev/null +++ b/openasip/src/applibs/TestGenerator/LimmTestSnippetGenerator.hh @@ -0,0 +1,107 @@ +/* + Copyright (c) 2002-2016 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. + */ +/* + * @file LimmTestSnippetGenerator.hh + * + * Declaration of LimmTestSnippetGenerator class. + * + * Created on: 2.8.2016 + * @author Henry Linjamäki 2016 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef LIMMTESTSNIPPETGENERATOR_HH +#define LIMMTESTSNIPPETGENERATOR_HH + +#include +#include +#include + +namespace TTAMachine { + class Machine; + class InstructionTemplate; + class ImmediateUnit; +} + +namespace TTAProgram { + class CodeSnippet; + class Procedure; + class Instruction; +} + +/* + * Generator that creates code snippet to load values into IUs specified in + * the instruction template. + */ +class LimmTestSnippetGenerator { +public: + LimmTestSnippetGenerator() = delete; + LimmTestSnippetGenerator( + const TTAMachine::InstructionTemplate& it); + virtual ~LimmTestSnippetGenerator(); + /** + * True, if the object is valid for use. Determined at construction time. + */ + bool valid() const { return valid_; } + + int inputCount() const; + int inputBitWidth(int i) const; + void assignInput(int i, uint64_t val); + + TTAProgram::CodeSnippet* generateSnippet() const; + +private: + + /// Indicates if the object is valid to generate snippets. + bool valid_ = false; + + /// Data used for snippet generation. One entry for each IU in the + /// instruction template. + struct targetIU { + /// The value to be loaded into IU + uint64_t testInput = 0; + // The maximum allowed value in bits to be loaded into IU. + int bitWidth = 0; + /// The targeted IU + const TTAMachine::ImmediateUnit& iu; + /// Lambda function to create instruction that does something to the + /// loaded value. + std::function sinkFn = nullptr; + + targetIU() = delete; + targetIU(int bitWidth, const TTAMachine::ImmediateUnit& iu) + : bitWidth(bitWidth), iu(iu) {} + }; + + std::vector testInputs_; + const TTAMachine::InstructionTemplate& it_; + + static std::function getSinkRoute( + const TTAMachine::ImmediateUnit& iu, int transportWidth); + static TTAProgram::Instruction* makeLimmLoadInstruction( + const std::vector& inputValues, + const TTAMachine::InstructionTemplate& it); +}; + +#endif /* LIMMTESTSNIPPETGENERATOR_HH */ diff --git a/openasip/src/applibs/TestGenerator/Makefile.am b/openasip/src/applibs/TestGenerator/Makefile.am new file mode 100644 index 0000000000..733d25d9dc --- /dev/null +++ b/openasip/src/applibs/TestGenerator/Makefile.am @@ -0,0 +1,46 @@ +noinst_LTLIBRARIES = libtege.la +libtege_la_SOURCES = TestGeneratorBase.cc TestCase.cc \ + VerificationDataGenerator.cc ImmediateTestGenerator.cc \ + TestGeneratorCollection.cc TestFileWriter.cc FUOperationTestGenerator.cc \ + ICacheTestGenerator.cc LimmTestSnippetGenerator.cc GuardTestGenerator.cc \ + SnippetGenerator.cc + +SRC_ROOT_DIR = $(top_srcdir)/src +BASE_DIR = ${SRC_ROOT_DIR}/base +APPLIBS_DIR = ${SRC_ROOT_DIR}/applibs + +MACH_DIR = ${BASE_DIR}/mach +UMACH_DIR = ${BASE_DIR}/umach +PROG_DIR = ${BASE_DIR}/program +BEM_DIR = ${BASE_DIR}/bem +IDF_DIR = ${BASE_DIR}/idf +TPEF_DIR = ${BASE_DIR}/tpef +OSAL_DIR = ${BASE_DIR}/osal +HDB_DIR = ${APPLIBS_DIR}/hdb +BEM_APPLIBS_DIR = ${APPLIBS_DIR}/bem +MACH_APPLIBS_DIR = ${APPLIBS_DIR}/mach +IDF_APPLIBS_DIR = ${APPLIBS_DIR}/idf +SIM_APPLIBS_DIR = ${APPLIBS_DIR}/Simulator +PIG_APPLIBS_DIR = ${APPLIBS_DIR}/PIG +TOOLS_DIR = ${SRC_ROOT_DIR}/tools +PLATFORM_DIR=${APPLIBS_DIR}/PlatformIntegrator + +AM_CPPFLAGS = -I${TOOLS_DIR} -I${MACH_DIR} -I${UMACH_DIR} -I${BEM_DIR} \ + -I${IDF_DIR} -I${TPEF_DIR} -I${OSAL_DIR} -I${HDB_DIR} \ + -I${BEM_APPLIBS_DIR} -I${MACH_APPLIBS_DIR} -I${IDF_APPLIBS_DIR} \ + -I${PLATFORM_DIR} -I${PROG_DIR} -I${SIM_APPLIBS_DIR} \ + -I${PIG_APPLIBS_DIR} + +dist-hook: + rm -rf $(distdir)/CVS $(distdir)/.deps $(distdir)/Makefile + +MAINTAINERCLEANFILES = *~ *.gcov *.bbg *.bb *.da + +## headers start +libtege_la_SOURCES += \ + TestGeneratorBase.hh TestCase.hh VerificationDataGenerator.hh \ + ImmediateTestGenerator.hh TestGeneratorCollection.hh \ + TestFileWriter.hh FUOperationTestGenerator.hh \ + ICacheTestGenerator.hh LimmTestSnippetGenerator.hh \ + GuardTestGenerator.hh SnippetGenerator.hh +## headers end diff --git a/openasip/src/applibs/TestGenerator/SnippetGenerator.cc b/openasip/src/applibs/TestGenerator/SnippetGenerator.cc new file mode 100644 index 0000000000..fb4251624b --- /dev/null +++ b/openasip/src/applibs/TestGenerator/SnippetGenerator.cc @@ -0,0 +1,302 @@ +/* + Copyright (c) 2002-2016 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. + */ +/* + * @file SnippetGenerator.cc + * + * Implementation of SnippetGenerator class. + * + * Created on: 5.8.2016 + * @author Henry Linjamäki 2016 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "SnippetGenerator.hh" + +#include + +#include "Machine.hh" +#include "RegisterFile.hh" +#include "Socket.hh" +#include "RFPort.hh" +#include "InstructionTemplate.hh" +#include "UniversalMachine.hh" + +#include "CodeSnippet.hh" +#include "Instruction.hh" +#include "Move.hh" +#include "TerminalFUPort.hh" +#include "TerminalRegister.hh" +#include "TerminalImmediate.hh" + +#include "MachineInfo.hh" +#include "MachineConnectivityCheck.hh" + +#include "MathTools.hh" +#include "SimValue.hh" + +using namespace TTAMachine; +using namespace TTAProgram; + +SnippetGenerator::SnippetGenerator() { +} + +SnippetGenerator::~SnippetGenerator() { +} + +/** + * Stores exactly the given value into the register. + * + * - No bits of the values are lost. + * - No machine resources are reserved/used after last instruction*1 + * + * *1 beside the targeted register. + */ +TTAProgram::CodeSnippet* +SnippetGenerator::getValue( + int64_t value, const TTAMachine::RegisterFile& rf, int regIdx) { + + // @note: Method implementation is quick and crude. + + using MCC = MachineConnectivityCheck; + using namespace TTAProgram; + + auto mach = getRealMachine(rf); + if (!mach) { + return nullptr; + } + + int valueBitWidthUnsigned = MathTools::requiredBits(value); + int valueBitWidthSigned = MathTools::requiredBitsSigned(value); + + std::unique_ptr code; + + // Attempt with SIMM // + for (auto& bus : mach->busNavigator()) { + // In non-matching bus-reg width negative values would lose its sign. + if (bus->width() != rf.width() + && (bus->width() < valueBitWidthUnsigned + || rf.width() < valueBitWidthUnsigned)) continue; + int requiredBitWidth = bus->signExtends()? + valueBitWidthSigned : valueBitWidthUnsigned; + if (bus->immediateWidth() < requiredBitWidth) continue; + if (!MCC::busConnectedToRF(*bus, rf)) continue; + + // Ok, use SIMM + auto simmInstr = getInstruction( + getUncheckedSimmMove(value, *bus, rf, regIdx)); + if (simmInstr) + return getCodeSnippet(simmInstr); + } + + // Attempt with LIMM // + // todo + + // Attempt with fabrication (by adding, shifting etc.) // + // todo + + // Attempt with load from constant pool // + // todo + + return nullptr; // All attempts failed +} + + +/** + * Returns new instruction with the move and suitable instruction template. + * + * Acquires ownership of the move regardless of the return result. + * + * Returns nullptr if the the instruction can not be created for some reason + * like: + * - No instruction template to use. + */ +TTAProgram::Instruction* +SnippetGenerator::getInstruction(TTAProgram::Move* m1) { + auto mach = getRealMachine(*m1); + if (!mach) return nullptr; + + using namespace TTAProgram; + + for (auto it : mach->instructionTemplateNavigator()) { + // TODO: search for template that has the most slots available for + // moves for flexibility to add other moves later. + if (!it->usesSlot(m1->bus().name()) + // Let's be conservative and not use IT writing to some IUs + // causing overwrites. + && it->numberOfDestinations() == 0) { + + std::unique_ptr instr(new Instruction(*it)); + instr->addMove(std::shared_ptr(m1)); + return instr.release(); + } + } + delete m1; + return nullptr; +} + +/** + * Returns sequence of instruction having only no-operation moves or + * equivalent. + */ +TTAProgram::CodeSnippet* +SnippetGenerator::getNOP(const TTAMachine::Machine& mach) { + for (auto it : mach.instructionTemplateNavigator()) { + if (it->numberOfDestinations() == 0) { + return getCodeSnippet(new Instruction(*it)); + } + } + return nullptr; +} + +/** + * Returns any port terminal connected to the bus to write into. + * + * Returned port terminal: + * - does not trigger operations, + * - has register index of zero in case of RF port. + */ +TTAProgram::Terminal* +SnippetGenerator::getAnyWritablePort( + const TTAMachine::Bus& bus) { + + using MCC = MachineConnectivityCheck; + + auto mach = getRealMachine(bus); + for (auto rf : mach->registerFileNavigator()) { + auto regTerm = getAnyWritablePort(bus, *rf, 0); + if (regTerm) return regTerm; + } + + for (auto fu : mach->functionUnitNavigator()) { + for (int i = 0; i < fu->portCount(); i ++) { + auto fuPort = fu->port(i); + if (!fuPort->isTriggering() + && MCC::busConnectedToPort(bus, *fuPort)) { + return new TerminalFUPort(*fuPort); + } + } + } + + return nullptr; +} + +/** + * Return first occurring writable port that is connected to the bus. + */ +TTAProgram::TerminalRegister* +SnippetGenerator::getAnyWritablePort( + const TTAMachine::Bus& bus, + const TTAMachine::RegisterFile& rf, + int registerIndex) { + + using MCC = MachineConnectivityCheck; + + auto mach = getRealMachine(rf); + if (!mach) return nullptr; + for (auto rfPort : MCC::findWritePorts(rf)) { + if (MCC::isConnected(bus, *rfPort)) { + return new TerminalRegister(*rfPort, registerIndex); + } + } + + return nullptr; +} + +/** + * Return real machine or nullptr. + */ +const TTAMachine::Machine* +SnippetGenerator::getRealMachine( + const TTAMachine::RegisterFile& rf) { + + if (rf.machine() && !rf.machine()->isUniversalMachine()) { + return rf.machine(); + } + return nullptr; +} + +/** + * Return real machine or nullptr. + */ +const TTAMachine::Machine* +SnippetGenerator::getRealMachine(const TTAProgram::Move& move) { + + auto* mach = move.bus().machine(); + if (mach && !mach->isUniversalMachine()) { + return mach; + } + return nullptr; +} + +/** + * Return real machine or nullptr. + */ +const TTAMachine::Machine* +SnippetGenerator::getRealMachine( + const TTAMachine::Bus& bus) { + auto* mach = bus.machine(); + if (mach && !mach->isUniversalMachine()) { + return mach; + } + return nullptr; +} + +/** + * Creates code snippet with the given instruction. + */ +TTAProgram::CodeSnippet* +SnippetGenerator::getCodeSnippet( + TTAProgram::Instruction* instr) { + + if (!instr) return nullptr; + + auto snippet = new TTAProgram::CodeSnippet(); + snippet->add(instr); + return snippet; +} + +/** + * Creates move to transport immediate to register. + * + * This method does not check its arguments for validity. + */ +TTAProgram::Move* +SnippetGenerator::getUncheckedSimmMove( + int64_t value, + TTAMachine::Bus& bus, + const TTAMachine::RegisterFile& rf, + int index) { + + using namespace TTAProgram; + + auto rfPortTerm = getAnyWritablePort(bus, rf, index); + if (!rfPortTerm) return nullptr; + + return new Move( + new TerminalImmediate(SimValue(value, rf.width())), + rfPortTerm, + bus); +} + + diff --git a/openasip/src/applibs/TestGenerator/SnippetGenerator.hh b/openasip/src/applibs/TestGenerator/SnippetGenerator.hh new file mode 100644 index 0000000000..e7c5580898 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/SnippetGenerator.hh @@ -0,0 +1,104 @@ +/* + Copyright (c) 2002-2016 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. + */ +/* + * @file SnippetGenerator.hh + * + * Declaration of SnippetGenerator class. + * + * Created on: 5.8.2016 + * @author Henry Linjamäki 2016 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef SNIPPETGENERATOR_HH +#define SNIPPETGENERATOR_HH + +#include + +namespace TTAMachine { + class Machine; + class Bus; + class RegisterFile; + class RFPort; +} + +namespace TTAProgram { + class CodeSnippet; + class Instruction; + class Move; + class Terminal; + class TerminalRegister; +} + +/* + * Helper class to generate various code snippets, instructions, moves and + * others for test code generation purpose. + * + * All public methods returns valid, complete generated objects or nullptr + * if generation failed. Therefore, return values should always checked for + * nullptr. Also, generally the public methods do not deal with universal + * machine part. + * + * Todo when implemented and needed: about basic resource management by + * resource exclusion object. + */ +class SnippetGenerator { +public: + SnippetGenerator(); + virtual ~SnippetGenerator(); + + static TTAProgram::CodeSnippet* getValue( + int64_t value, const TTAMachine::RegisterFile&, int registerIndex); + + static TTAProgram::Instruction* getInstruction( + TTAProgram::Move* m1); + + static TTAProgram::CodeSnippet* getNOP(const TTAMachine::Machine& mach); + + static TTAProgram::Terminal* getAnyWritablePort( + const TTAMachine::Bus& bus); + + static TTAProgram::TerminalRegister* getAnyWritablePort( + const TTAMachine::Bus& bus, + const TTAMachine::RegisterFile& rf, + int registerIndex); + +private: + + static const TTAMachine::Machine* getRealMachine( + const TTAMachine::RegisterFile& rf); + static const TTAMachine::Machine* getRealMachine( + const TTAProgram::Move& move); + static const TTAMachine::Machine* getRealMachine( + const TTAMachine::Bus& bus); + static TTAProgram::CodeSnippet* getCodeSnippet( + TTAProgram::Instruction* instr); + static TTAProgram::Move* getUncheckedSimmMove( + int64_t value, + TTAMachine::Bus& bus, + const TTAMachine::RegisterFile&, + int index); +}; + +#endif /* SNIPPETGENERATOR_HH */ diff --git a/openasip/src/applibs/TestGenerator/TestCase.cc b/openasip/src/applibs/TestGenerator/TestCase.cc new file mode 100644 index 0000000000..ceaca53a49 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/TestCase.cc @@ -0,0 +1,103 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestCase.cc + * + * Implementation of TestCase class. + * + * Created on: 26.2.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "TestCase.hh" + +#include "AddressSpace.hh" +#include "Program.hh" +#include "Procedure.hh" + +/** + * Constructs an empty test case. + */ +TestCase::TestCase() + : program_(NULL), + testname_(std::string()) { +} + +/** + * Constructs a test case using given program as test case. + * + * @param program The test case program + * @param testname The name of the test case + * @param generateVerificationData By default verification data is generated + * automatically. If set to false this process + * should be skipped. + * Takes ownership of the program. + */ +TestCase::TestCase( + TTAProgram::Program* program, + const std::string& testname, + bool generateVerificationData) + : program_(program), + testname_(testname), + generateVerificationData_(generateVerificationData) { +} + +/** + * Constructs a test case with preassigned program. + */ +TestCase::TestCase( + const TTAMachine::AddressSpace& space, + const std::string& testname) + : program_(new TTAProgram::Program(space)), + testname_(testname) { + + program_->addProcedure(new TTAProgram::Procedure("main", space)); +} + +/** + * Destructor. + */ +TestCase::~TestCase() { +} + +void TestCase::setProgram(TTAProgram::Program* program) { + program_ = program; +} + +TTAProgram::Program* TestCase::program() { + return program_; +} + +const TTAProgram::Program* TestCase::program() const { + return program_; +} + +void TestCase::setTestName(const std::string& newName) { + testname_ = newName; +} + +const std::string& TestCase::testName() const { + return testname_; +} diff --git a/openasip/src/applibs/TestGenerator/TestCase.hh b/openasip/src/applibs/TestGenerator/TestCase.hh new file mode 100644 index 0000000000..6970863f04 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/TestCase.hh @@ -0,0 +1,77 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestCase.hh + * + * Declaration of TestCase class. + * + * Created on: 26.2.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef TESTCASE_HH +#define TESTCASE_HH + +#include + +namespace TTAProgram { + class Program; +} + +namespace TTAMachine { + class AddressSpace; +} + +/* + * The class representing a test case. + */ +class TestCase { +public: + TestCase(); + TestCase( + TTAProgram::Program* program, + const std::string& testname = std::string(), + bool generateVerificationData = true); + TestCase( + const TTAMachine::AddressSpace& space, + const std::string& testname = std::string()); + virtual ~TestCase(); + + void setProgram(TTAProgram::Program* program); + TTAProgram::Program* program(); + const TTAProgram::Program* program() const; + void setTestName(const std::string& newName); + const std::string& testName() const; + bool generateVerificationData() const { return generateVerificationData_; } + +private: + TTAProgram::Program* program_; + std::string testname_; + /// The flag to tell if verification data should be generated. This mean + /// the data has not been created or it is not wanted. + bool generateVerificationData_ = true; +}; + +#endif /* TESTCASE_HH */ diff --git a/openasip/src/applibs/TestGenerator/TestFileWriter.cc b/openasip/src/applibs/TestGenerator/TestFileWriter.cc new file mode 100644 index 0000000000..fdb6403c9c --- /dev/null +++ b/openasip/src/applibs/TestGenerator/TestFileWriter.cc @@ -0,0 +1,258 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestFileWriter.cc + * + * Implementation of TestFileWriter class. + * + * Created on: 12.3.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "TestFileWriter.hh" + +#include + +#include "Machine.hh" +#include "VerificationDataGenerator.hh" +#include "ProgramImageGenerator.hh" +#include "BEMGenerator.hh" +#include "Program.hh" +#include "ProgramWriter.hh" +#include "TestCase.hh" +#include "BinaryEncoding.hh" +#include "ControlUnit.hh" +#include "POMDisassembler.hh" +#include "Environment.hh" + +/** + * Constructor. + */ +TestFileWriter::TestFileWriter() + : outputDirectory_(""), + testCase_(NULL), + machine_(NULL), + pig_(), + imageFormat_(ProgramImageGenerator::ASCII), + bem_(NULL) { +} + +/** + * Destructor. + */ +TestFileWriter::~TestFileWriter() { +} + +/** + * Sets output directory for services. + */ +void +TestFileWriter::setOutput(const Path& directory) { + outputDirectory_ = directory; + verifGen_.setOutputDirectory(outputDirectory_); + return; +} + +/** + * Sets test case to be processed. + */ +void +TestFileWriter::setTestCase(const TestCase& testCase) { + testCase_ = &testCase; + return; +} + +/** + * Sets the architecture. + * + * Needed by makeVerificationData() function. + */ +void +TestFileWriter::setMachine(const TTAMachine::Machine& machine) { + machine_ = &machine; + verifGen_.setMachine(*machine_); + BEMGenerator bemGenerator(*machine_); + if (bem_ != NULL) { + delete bem_; bem_ = NULL; + } + bem_ = bemGenerator.generate(); + pig_.loadBEM(*bem_); + pig_.loadMachine(*machine_); + return; +} + +/** + * Sets image format of the program and data by reading string description. + */ +void +TestFileWriter::setImageFormat(ProgramImageGenerator::OutputFormat format) { + imageFormat_ = format; + return; +} + +/** + * Sets data memory width in MAUs. + */ +void +TestFileWriter::setDataMemoryWidthInMAUs(int widthInMAUs) { + dataMemoryWidthInMAUs_ = widthInMAUs; + return; +} + +/** + * Writes verification data of current TestCase. + * + * setTestCase() and setMachine() must be called before this. + */ +void +TestFileWriter::makeVerificationData() { + assert(testCase_ != NULL && "Unset test case."); + assert(machine_ != NULL && "Unset machine."); + + verifGen_.generate(*testCase_); + return; +} + +/** + * Writes program and data images from current TestCase. + * + * setTestCase() and setMachine() must be called before this. + */ +void +TestFileWriter::makeProgramImages() { + assert(testCase_ != NULL && "Unset test case."); + assert(machine_ != NULL && "Unset machine."); + using TTAProgram::ProgramWriter; + using TTAMachine::Machine; + + stringCRef testname = testCase_->testName(); + ProgramImageGenerator::TPEFMap tpefMap; + tpefMap[testname] = ProgramWriter::createBinary(*testCase_->program()); + pig_.loadPrograms(tpefMap); + + // Generate program image // + Path filePath = outputDirectory_; + filePath /= (testname + formatExtension(imageFormat_)); + std::ofstream imageStream(filePath.string().c_str()); + pig_.generateProgramImage(testname, imageStream, imageFormat_); + imageStream.close(); + + // Generate Data Images // + Machine::AddressSpaceNavigator asNav = machine_->addressSpaceNavigator(); + for (int i = 0; i < asNav.count(); i++) { + std::string asName = asNav.item(i)->name(); + if (machine_->controlUnit()->addressSpace()->name() == asName) { + continue; // Skip instruction memory address space. + } + + filePath = outputDirectory_; + filePath /= (testname + "_" + asName + formatExtension(imageFormat_)); + imageStream.open(filePath.string().c_str()); + pig_.generateDataImage( + testname, + *tpefMap[testname], + asName, imageStream, + imageFormat_, + dataMemoryWidthInMAUs_, + false); + imageStream.close(); + } + return; +} + +/** + * Writes disassembly of current TestCase. + */ +void +TestFileWriter::makeDisassembly() { + assert(testCase_ != NULL && "Unset test case."); + + Path filePath = outputDirectory_; + filePath /= (testCase_->testName() + ".tceasm"); + std::ofstream disasmFile(filePath.string().c_str()); + disasmFile << POMDisassembler::disassembleFully( + *testCase_->program(), true); + disasmFile.close(); + + return; +} + +/** + * Writes TPEF of current TestCase. + */ +void +TestFileWriter::makeTPEF() { + Path filePath = outputDirectory_; + filePath /= (testCase_->testName() + ".tpef"); + TTAProgram::Program::writeToTPEF(*testCase_->program(), filePath); +} + +/** + * Writes testrunner script that runs the generated test cases in RTL-simulator. + * + * @param altDirectory Overrides the target output directory set by setOutput(). + */ +void +TestFileWriter::makeTestRunnerScript(const Path altDirectory) { + + Path src = Path(Environment::dataDirPath("TeGe"))/"testrunner.py"; + Path dst; + if (altDirectory.empty()) { + dst = outputDirectory_; + } else { + dst = altDirectory; + } + dst /= src.filename(); + FileSystem::copy(src.string(), dst.string()); +} + +/** + * Returns file extension string for given output image format. + */ +std::string +TestFileWriter::formatExtension(ProgramImageGenerator::OutputFormat format) { + std::string extension; + switch(format) { + case ProgramImageGenerator::BINARY: + case ProgramImageGenerator::ASCII: + case ProgramImageGenerator::ARRAY: + extension = ".img"; + break; + case ProgramImageGenerator::MIF: + extension = ".mif"; + break; + case ProgramImageGenerator::VHDL: + extension = "_pkg.vhdl"; + break; + case ProgramImageGenerator::COE: + extension = ".coe"; + break; + default: + assert(false && "Unknown image format."); + break; + } + return extension; +} + diff --git a/openasip/src/applibs/TestGenerator/TestFileWriter.hh b/openasip/src/applibs/TestGenerator/TestFileWriter.hh new file mode 100644 index 0000000000..e09e94d382 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/TestFileWriter.hh @@ -0,0 +1,90 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestFileWriter.hh + * + * Declaration of TestFileWriter class. + * + * Created on: 12.3.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef TESTFILEWRITER_HH +#define TESTFILEWRITER_HH + +#include "TCEString.hh" +#include "VerificationDataGenerator.hh" +#include "ProgramImageGenerator.hh" +#include "FileSystem.hh" + +namespace TTAMachine { + class Machine; +} + +class TestCase; + +/* + * Services to write various automated test generator related files. + */ +class TestFileWriter { +public: + TestFileWriter(); + virtual ~TestFileWriter(); + + void setOutput(const Path& directory); + void setTestCase(const TestCase& testCase); + void setMachine(const TTAMachine::Machine& machine); + void setImageFormat(ProgramImageGenerator::OutputFormat format); + void setDataMemoryWidthInMAUs(int widthInMAUs); + + void makeVerificationData(); + void makeProgramImages(); + void makeDisassembly(); + void makeTPEF(); + void makeTestRunnerScript(const Path altDirectory = Path()); + +private: + + std::string formatExtension(ProgramImageGenerator::OutputFormat format); + + /// Output directory for test files. + Path outputDirectory_; + /// The current test case to process. + const TestCase* testCase_; + /// The current target architecture. + const TTAMachine::Machine* machine_; + /// The verification data generator. + VerificationDataGenerator verifGen_; + /// The program image generator. + ProgramImageGenerator pig_; + /// The output image format of the program and data. + ProgramImageGenerator::OutputFormat imageFormat_; + /// Binary encoding for The program image generator. + BinaryEncoding* bem_; + /// Data memory width in MAUs. + int dataMemoryWidthInMAUs_; +}; + +#endif /* TESTFILEWRITER_HH */ diff --git a/openasip/src/applibs/TestGenerator/TestGeneratorBase.cc b/openasip/src/applibs/TestGenerator/TestGeneratorBase.cc new file mode 100644 index 0000000000..fd097595ff --- /dev/null +++ b/openasip/src/applibs/TestGenerator/TestGeneratorBase.cc @@ -0,0 +1,130 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestGeneratorBase.cc + * + * Implementation of TestGeneratorBase class. + * + * Created on: 25.2.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "TestGeneratorBase.hh" + +#include + +/** + * Main constructor. + */ +TestGeneratorBase::TestGeneratorBase( + const std::string& generatortitle, + const std::string& description, + bool enabledByDefault) + + : generatortitle_(generatortitle), + description_(description), + machine_(NULL), + implementation_(NULL), + enabled_(enabledByDefault) { +} + +/** + * Destructor. + */ +TestGeneratorBase::~TestGeneratorBase() { +} + + +const std::string& +TestGeneratorBase::title() const { + return generatortitle_; +} + + +const std::string& +TestGeneratorBase::description() const { + return description_; +} + + +const TTAMachine::Machine& +TestGeneratorBase::machine() const { + return *machine_; +} + + +void +TestGeneratorBase::setMachine(const TTAMachine::Machine& machine) { + machine_ = &machine; +} + +const IDF::MachineImplementation& +TestGeneratorBase::implementation() const { + return *implementation_; +} + + +void +TestGeneratorBase::setImplementation( + const IDF::MachineImplementation& implementation) { + implementation_ = &implementation; +} + + +bool +TestGeneratorBase::isEnabled() const { + return enabled_; +} + + +void +TestGeneratorBase::setEnabled(bool setting) { + enabled_ = setting; +} + + +std::vector +TestGeneratorBase::generateTestCases( + RandomNumberGenerator::SeedType seed) { + + if (initializedWithAdf()) { + return generateTestCasesImpl(seed); + } else { + return std::vector(); // Return empty. + } +} + + +bool +TestGeneratorBase::initializedWithAdf() const { + return machine_ != NULL; +} + + +bool +TestGeneratorBase::initializedWithIdf() const { + return implementation_ != NULL; +} + diff --git a/openasip/src/applibs/TestGenerator/TestGeneratorBase.hh b/openasip/src/applibs/TestGenerator/TestGeneratorBase.hh new file mode 100644 index 0000000000..83de387f96 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/TestGeneratorBase.hh @@ -0,0 +1,101 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestGeneratorBase.hh + * + * Declaration of TestGeneratorBase class. + * + * Created on: 25.2.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef TESTGENERATORBASE_HH +#define TESTGENERATORBASE_HH + +#include +#include + +#include "Application.hh" +#include "RandomNumberGenerator.hh" +#include "TestCase.hh" + +namespace TTAMachine { + class Machine; +} + +namespace TTAProgram { + class Program; + class CodeSnippet; +} + +namespace IDF { + class MachineImplementation; +} + +class TestGeneratorBase { +public: + + TestGeneratorBase( + const std::string& generatortitle, + const std::string& description, + bool enabledByDefault = true); + virtual ~TestGeneratorBase(); + + const std::string& title() const; + const std::string& description() const; + const TTAMachine::Machine& machine() const; + void setMachine(const TTAMachine::Machine& machine); + const IDF::MachineImplementation& implementation() const; + void setImplementation(const IDF::MachineImplementation& implementation); + + bool isEnabled() const; + void setEnabled(bool setting = true); + + std::vector generateTestCases( + RandomNumberGenerator::SeedType seed = + RandomNumberGenerator::DEFAULTSEED); + +protected: + bool initializedWithAdf() const; + bool initializedWithIdf() const; + + virtual std::vector generateTestCasesImpl( + RandomNumberGenerator::SeedType seed = + RandomNumberGenerator::DEFAULTSEED) =0; + +private: + /// The title of test generator serving as identification. + const std::string generatortitle_; + /// The description of test generator. + const std::string description_; + /// The architecture for which the test cases are to be generated. + const TTAMachine::Machine* machine_; + /// The optional implementation of the architecture. NULL if not defined. + const IDF::MachineImplementation* implementation_; + + bool enabled_; +}; + +#endif /* TESTGENERATORBASE_HH */ diff --git a/openasip/src/applibs/TestGenerator/TestGeneratorCollection.cc b/openasip/src/applibs/TestGenerator/TestGeneratorCollection.cc new file mode 100644 index 0000000000..157e498194 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/TestGeneratorCollection.cc @@ -0,0 +1,162 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestGeneratorCollection.cc + * + * Implementation of TestGeneratorCollection class. + * + * Created on: 11.3.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "TestGeneratorCollection.hh" + +#include +#include + +#include "MapTools.hh" +#include "TCEString.hh" + +#include "ImmediateTestGenerator.hh" +#include "FUOperationTestGenerator.hh" +#include "ICacheTestGenerator.hh" +#include "GuardTestGenerator.hh" + + +TestGeneratorCollection::~TestGeneratorCollection() { + deleteTestGenerators(testGenerators_); +} + + +TestGeneratorCollection::TestGeneratorCollection() + : machine_(NULL), + implementation_(NULL) { + + loadDefaultTestGenerators(testGenerators_); +} + +void +TestGeneratorCollection::setMachine(const TTAMachine::Machine& targetMachine) { + deleteTestGenerators(testGenerators_); + machine_ = &targetMachine; + loadDefaultTestGenerators(testGenerators_); + setMachine(targetMachine, testGenerators_); +} + +void +TestGeneratorCollection::setImplementation( + const IDF::MachineImplementation& implementation) { + implementation_ = &implementation; + setImplementation(implementation, testGenerators_); +} + +std::vector +TestGeneratorCollection::generateTestCases( + RandomNumberGenerator::SeedType seed) { + std::vector testcases; + GeneratorMap::iterator genIt; + for (auto generator : testGenerators_) { + if (generator.second->isEnabled()) { + std::vector newSetOfTestCases = + generator.second->generateTestCases(seed); + // Todo: test case name conflict check here? + testcases.insert(testcases.end(), + newSetOfTestCases.begin(), newSetOfTestCases.end()); + } + } + return testcases; +} + + +const TestGeneratorCollection::GeneratorMap& +TestGeneratorCollection::getGenerators() const { + return testGenerators_; +} + + +void +TestGeneratorCollection::setEnabled( + const GeneratorKey& generatorKey, bool setting) { + + if (testGenerators_.count(generatorKey) == 0) { + return; + } else { + testGenerators_[generatorKey]->setEnabled(setting); + } +} + + +void +TestGeneratorCollection::loadDefaultTestGenerators( + GeneratorMap& generatorsOut) { + + std::insert_iterator containerInserter = + std::inserter(generatorsOut, generatorsOut.end()); + + containerInserter = create_pair(new ImmediateTestGenerator()); + containerInserter = create_pair(new FUOperationTestGenerator()); + containerInserter = create_pair(new ICacheTestGenerator()); + containerInserter = create_pair(new GuardTestGenerator()); +} + + +void +TestGeneratorCollection::setMachine( + const TTAMachine::Machine& targetMachine, + GeneratorMap& generators) { + + GeneratorMap::iterator it; + for (it = generators.begin(); it != generators.end(); it++) { + it->second->setMachine(targetMachine); + } +} + +void +TestGeneratorCollection::setImplementation( + const IDF::MachineImplementation& implementation, + GeneratorMap& generators) { + + GeneratorMap::iterator it; + for (it = generators.begin(); it != generators.end(); it++) { + it->second->setImplementation(implementation); + } +} + +std::pair +TestGeneratorCollection::create_pair(TestGeneratorBase* newGenerator) { + return std::make_pair(newGenerator->title(), newGenerator); +} + + +void +TestGeneratorCollection::deleteTestGenerators(GeneratorMap& generators) { + while (!generators.empty()) { + GeneratorMap::iterator it = generators.begin(); + TestGeneratorBase* to_remove = it->second; + it->second = NULL; + generators.erase(it); + delete to_remove; + } +} diff --git a/openasip/src/applibs/TestGenerator/TestGeneratorCollection.hh b/openasip/src/applibs/TestGenerator/TestGeneratorCollection.hh new file mode 100644 index 0000000000..dcfcaa90e9 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/TestGeneratorCollection.hh @@ -0,0 +1,92 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestGeneratorCollection.hh + * + * Declaration of TestGeneratorCollection class. + * + * Created on: 11.3.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef TESTGENERATORCOLLECTION_HH +#define TESTGENERATORCOLLECTION_HH + +#include +#include +#include + +#include "TestGeneratorBase.hh" +#include "TestCase.hh" +#include "RandomNumberGenerator.hh" + +namespace TTAMachine { + class Machine; +} + +namespace IDF { + class MachineImplementation; +} + +/** + * The container class for test generators. + */ +class TestGeneratorCollection { +public: + typedef std::string GeneratorKey; // Id string from test generators' as key + typedef std::map GeneratorMap; + + TestGeneratorCollection(); + virtual ~TestGeneratorCollection(); + + void setMachine(const TTAMachine::Machine& targetMachine); + void setImplementation(const IDF::MachineImplementation& implementation); + std::vector generateTestCases( + RandomNumberGenerator::SeedType seed = + RandomNumberGenerator::DEFAULTSEED); + const GeneratorMap& getGenerators() const; + void setEnabled(const GeneratorKey& generatorKey, bool setting = true); + +private: + void loadDefaultTestGenerators(GeneratorMap& generatorsOut); + static void setMachine( + const TTAMachine::Machine& targetMachine, + GeneratorMap& generators); + static void setImplementation( + const IDF::MachineImplementation& implementation, + GeneratorMap& generators); + static std::pair create_pair( + TestGeneratorBase* newGenerator); + static void deleteTestGenerators(GeneratorMap& generators); + + /// The target architecture for the tests. + const TTAMachine::Machine* machine_; + /// The implementation of the architecture. + const IDF::MachineImplementation* implementation_; + /// The testGenerator container. + GeneratorMap testGenerators_; +}; + +#endif /* TESTGENERATORCOLLECTION_HH */ diff --git a/openasip/src/applibs/TestGenerator/VerificationDataGenerator.cc b/openasip/src/applibs/TestGenerator/VerificationDataGenerator.cc new file mode 100644 index 0000000000..04162fedd1 --- /dev/null +++ b/openasip/src/applibs/TestGenerator/VerificationDataGenerator.cc @@ -0,0 +1,150 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file VerificationDataGenerator.cc + * + * Implementation of VerificationDataGenerator class. + * + * Created on: 26.2.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "VerificationDataGenerator.hh" +#include "BusTracker.hh" + +#include "Machine.hh" +#include "Program.hh" +#include "TestCase.hh" + +VerificationDataGenerator::VerificationDataGenerator( + unsigned int timeOutMs) + : simulator_(), + outputDir_(""), + busTraceStream_(), + busTracker_(simulator_, busTraceStream_) { + + simulator_.setTimeout(timeOutMs); +} + +/** + * Constructor. + */ +VerificationDataGenerator::VerificationDataGenerator( + const TTAMachine::Machine& machine, + const Path& outputDir, + unsigned int timeOutMs) + : simulator_(), + outputDir_(outputDir), + busTraceStream_(), + busTracker_(simulator_, busTraceStream_) { + + simulator_.loadMachine(machine); + simulator_.setTimeout(timeOutMs); +} + +VerificationDataGenerator::~VerificationDataGenerator() { +} + +/** + * Writes verification data for given program. + * + * Verification data includes only bus trace currently. + * + * @param TestCase Uses the program in simulation and testname as prefix in + * verification data files (e.g. {testname}.bustrace) + * @exception IOException If opening files to write fails. + * @exception SimulationException If error occurs in the simulator. + */ +void +VerificationDataGenerator::generate(const TestCase& testCase) { + Path busTraceFile(outputDir_); + busTraceFile /= testCase.testName() + ".bustrace"; + Path cycleCountFile(outputDir_); + cycleCountFile /= testCase.testName() + ".cyclecount"; + std::ofstream cycleCountStream; + reopen(busTraceStream_, busTraceFile); + reopen(cycleCountStream, cycleCountFile); + if (simulator_.isSimulationInitialized() + && !simulator_.hasSimulationEnded()) { + simulator_.killSimulation(); + } + try { + simulator_.loadProgram(*testCase.program()); + } catch (Exception& e) { + if (busTraceStream_.is_open()) { + busTraceStream_.close(); + } + if (FileSystem::fileExists(busTraceFile.string())) { + FileSystem::removeFileOrDirectory(busTraceFile.string()); + } + THROW_EXCEPTION(IOException, e.errorMessage()); + } + try { + simulator_.run(); + cycleCountStream << simulator_.cycleCount() << std::endl; + } catch (Exception& e) { + if (busTraceStream_.is_open()) { + busTraceStream_.close(); + } + if (FileSystem::fileExists(busTraceFile.string())) { + FileSystem::removeFileOrDirectory(busTraceFile.string()); + } + THROW_EXCEPTION(SimulationException, e.errorMessage()); + } + + if (!simulator_.hasSimulationEnded()) { + // Todo message simulation failure or throw exception. + } + + cycleCountStream.close(); +} + +void +VerificationDataGenerator::setMachine(const TTAMachine::Machine& machine) { + simulator_.loadMachine(machine); +} + +void +VerificationDataGenerator::setOutputDirectory(const Path& outputDir) { + outputDir_ = outputDir; +} + +/** + * Reopens file stream to given file of which data will be overwritten. + * + * @exception IOException If opening file to write fails. + */ +void +VerificationDataGenerator::reopen(std::ofstream& stream, const Path& file) { + if (stream.is_open()) { + stream.close(); + } + stream.clear(); + stream.open(file.string().c_str()); + if (!stream.is_open()) { + throw IOException(__FILE__, __LINE__, __func__, + std::string("Unable to write to file ") + file.string().c_str()); + } +} diff --git a/openasip/src/applibs/TestGenerator/VerificationDataGenerator.hh b/openasip/src/applibs/TestGenerator/VerificationDataGenerator.hh new file mode 100644 index 0000000000..8fb2233ddb --- /dev/null +++ b/openasip/src/applibs/TestGenerator/VerificationDataGenerator.hh @@ -0,0 +1,87 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file VerificationDataGenerator.hh + * + * Implementation of VerificationDataGenerator class. + * + * Created on: 26.2.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef VERIFICATIONDATAGENERATOR_HH +#define VERIFICATIONDATAGENERATOR_HH + +#include + +#include "Exception.hh" +#include "SimulatorFrontend.hh" +#include "BusTracker.hh" +#include "FileSystem.hh" + +namespace TTAMachine { + class Machine; +} + +namespace TTAProgram { + class Program; +} + +class TestCase; +class Path; +class BusTracker; +class SimulatorFrontend; + + +/* + * Class that writes verification data from Programs. + */ +class VerificationDataGenerator { +public: + VerificationDataGenerator(unsigned int timeOutMs = 1000); + VerificationDataGenerator( + const TTAMachine::Machine& machine, + const Path& outputDir, + unsigned int timeOutMs = 1000); + virtual ~VerificationDataGenerator(); + void setMachine(const TTAMachine::Machine& machine); + void setOutputDirectory(const Path& outputDir); + virtual void generate(const TestCase& testCase); + +protected: + /// The simulator used to produce verification data. + SimulatorFrontend simulator_; + /// The path to directory where the verification data are placed. + Path outputDir_; + /// File stream for bus trace. + std::ofstream busTraceStream_; + /// Generates bus trace verification data. + BusTracker busTracker_; + +private: + void reopen(std::ofstream& stream, const Path& file); +}; + +#endif /* VERIFICATIONDATAGENERATOR_HH */ diff --git a/openasip/src/applibs/idf/IDFValidator.cc b/openasip/src/applibs/idf/IDFValidator.cc index f38cc2d910..c3c67f75ac 100644 --- a/openasip/src/applibs/idf/IDFValidator.cc +++ b/openasip/src/applibs/idf/IDFValidator.cc @@ -152,7 +152,8 @@ IDFValidator::checkRFImplementations() { for (int i = 0; i < rfNav.count(); i++) { RegisterFile* rf = rfNav.item(i); - if (!idf_.hasRFImplementation(rf->name())) { + if (!idf_.hasRFImplementation(rf->name()) && + !idf_.hasRFGeneration(rf->name())) { format errorMsg( "IDF does not define an implementation for " "register file %1%."); diff --git a/openasip/src/base/idf/ArbiterImplementation.cc b/openasip/src/base/idf/ArbiterImplementation.cc new file mode 100644 index 0000000000..12861029f0 --- /dev/null +++ b/openasip/src/base/idf/ArbiterImplementation.cc @@ -0,0 +1,136 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file ArbiterImplementation.cc + * + * Implementation of ArbiterImplementation class. + * + * Created on: 7.9.2015 + * @author Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "ArbiterImplementation.hh" + +#include "ObjectState.hh" + +namespace IDF { + +const std::string ArbiterImplementation::OSNAME_ARBITER_IMPLEMENTATION = + "instruction-arbiter"; +const std::string ArbiterImplementation::OSKEY_ARBITRATION_SCHEME = + "arbitration-scheme"; +const std::string ArbiterImplementation::OSKEY_MAX_TIME_SLICE = + "maximum-time-slice"; + +ArbiterImplementation::ArbiterImplementation() { +} + +ArbiterImplementation::ArbiterImplementation(const ObjectState* state) { + loadState(state); +} + +ArbiterImplementation::~ArbiterImplementation() { +} + + +ArbiterImplementation::ArbitrationScheme +ArbiterImplementation::arbitrationScheme() const { + return arbitrationScheme_; +} + +void +ArbiterImplementation::setArbitrationScheme(ArbitrationScheme scheme) { + arbitrationScheme_ = scheme; +} + +unsigned +ArbiterImplementation::maximumTimeSlice() const { + return maximumTimeSlice_; +} + +void +ArbiterImplementation::setMaximumTimeSlice(unsigned value) { + maximumTimeSlice_ = value; +} + +void +ArbiterImplementation::loadState(const ObjectState* state) { + if (state->name() != OSNAME_ARBITER_IMPLEMENTATION) { + THROW_EXCEPTION(ObjectStateLoadingException, "Invalid object state."); + } + try { + arbitrationScheme_ = unserializeArbitrationScheme( + state->childByName(OSKEY_ARBITRATION_SCHEME)->stringValue()); + if (state->hasChild(OSKEY_MAX_TIME_SLICE)) { + maximumTimeSlice_ = + state->childByName(OSKEY_MAX_TIME_SLICE)->unsignedIntValue(); + } else { + maximumTimeSlice_ = 0; + } + } catch (const Exception& exception) { + THROW_EXCEPTION(ObjectStateLoadingException, exception.errorMessage()); + } +} + +ObjectState* +ArbiterImplementation::saveState() const { + ObjectState* state = new ObjectState(OSNAME_ARBITER_IMPLEMENTATION); + + ObjectState* arbScheme = new ObjectState(OSKEY_ARBITRATION_SCHEME); + arbScheme->setValue(serialize(arbitrationScheme_)); + state->addChild(arbScheme); + + if (maximumTimeSlice_ > 0) { + ObjectState* maxTimeSlice = new ObjectState(OSKEY_MAX_TIME_SLICE); + maxTimeSlice->setValue(maximumTimeSlice_); + state->addChild(maxTimeSlice); + } + + return state; +} + +ArbiterImplementation::ArbitrationScheme +ArbiterImplementation::unserializeArbitrationScheme(const std::string& str) { + if (str == "round-robin") { + return ArbiterImplementation::ROUND_ROBIN; + } else { + THROW_EXCEPTION(NoKnownConversion, "Unknown arbitration scheme: " + + str + "."); + } +} + +std::string +ArbiterImplementation:: serialize( + ArbiterImplementation::ArbitrationScheme scheme) { + + if (scheme == ROUND_ROBIN) { + return "round-robin"; + } else { + assert(false && "unknown arbitration scheme."); + return ""; + } +} + +} /* namespace ProGe */ diff --git a/openasip/src/base/idf/ArbiterImplementation.hh b/openasip/src/base/idf/ArbiterImplementation.hh new file mode 100644 index 0000000000..2a7ca53f4d --- /dev/null +++ b/openasip/src/base/idf/ArbiterImplementation.hh @@ -0,0 +1,85 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file ArbiterImplementarion.hh + * + * Declaration of ArbiterImplementation class. + * + * Created on: 7.9.2015 + * @author Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef ARBITERIMPLEMENTARION_HH +#define ARBITERIMPLEMENTARION_HH + +#include + +#include "Exception.hh" +#include "Serializable.hh" + +namespace IDF { + +/* + * todo + */ +class ArbiterImplementation: public Serializable { +public: + + enum ArbitrationScheme { ROUND_ROBIN }; + + ArbiterImplementation(); + ArbiterImplementation(const ObjectState* state); + virtual ~ArbiterImplementation(); + + ArbitrationScheme arbitrationScheme() const; + void setArbitrationScheme(ArbitrationScheme scheme); + unsigned maximumTimeSlice() const; + void setMaximumTimeSlice(unsigned value); + + void loadState(const ObjectState* state) override; + ObjectState* saveState() const override; + + /// ObjectState name for this class. + static const std::string OSNAME_ARBITER_IMPLEMENTATION; + /// ObjectState name for arbitration scheme. + static const std::string OSKEY_ARBITRATION_SCHEME; + /// ObjectState name for maximum time slice. + static const std::string OSKEY_MAX_TIME_SLICE; + +private: + static ArbitrationScheme unserializeArbitrationScheme( + const std::string& str); + static std::string serialize(ArbitrationScheme scheme); + + /// The arbitration scheme. + ArbitrationScheme arbitrationScheme_ = ROUND_ROBIN; + /// The maximum period given for processor in time sliced arbitration. + unsigned maximumTimeSlice_ = 0; + +}; + +} /* namespace ProGe */ + +#endif /* ARBITERIMPLEMENTARION_HH_ */ diff --git a/openasip/src/base/idf/CacheImplementation.cc b/openasip/src/base/idf/CacheImplementation.cc new file mode 100644 index 0000000000..969e124c9f --- /dev/null +++ b/openasip/src/base/idf/CacheImplementation.cc @@ -0,0 +1,246 @@ +/* + Copyright (c) 2002-2015 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. + */ +/** + * @file CacheImplementation.cc + * + * Implementation of CacheImplementation. + * + * @author Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "CacheImplementation.hh" + +#include +#include + +#include "ObjectState.hh" + +namespace IDF { + +const std::string CacheImplementation::OSNAME_CACHE_IMPLEMENTATION + = "cache_implementation"; +const std::string CacheImplementation::OSKEY_BLOCK_SIZE + = "cache_block_size"; +const std::string CacheImplementation::OSKEY_SET_SIZE + = "cache_set_size"; +const std::string CacheImplementation::OSKEY_CACHE_SIZE + = "cache_size"; +const std::string CacheImplementation::OSKEY_REPLACEMENT_POLICY + = "cache_replacement_policy"; + +namespace /* anonymous */ { + +using RepPolicy = CacheImplementation::ReplacementPolicy; +const std::map replacementPolicyToString{ + { RepPolicy::RANDOM, "random" }, + { RepPolicy::LRU, "LRU" } +}; + +const std::map stringToReplacementPolicy{ + { "random", RepPolicy::RANDOM }, + { "LRU", RepPolicy::LRU } +}; + +} // anonymous namespace + +/** + * Default constructor. + */ +CacheImplementation::CacheImplementation() +: CacheImplementation(1, 1, 1, ReplacementPolicy::INFERRED) { +} + +/** + * + * @param blockSize The size of a block/entry/line in words. Must be non-zero. + * @param setSize The size of a set/number of ways. Must be non-zero. + * @param cacheSize The size of the cache in number of sets. Must be non-zero. + * @param replacementPolicy The replacement policy. + */ +CacheImplementation::CacheImplementation( + unsigned blockSize, + unsigned setSize, + unsigned cacheSize, + ReplacementPolicy replacementPolicy) + : blockSize_(blockSize), + setSize_(setSize), + cacheSize_(cacheSize), + replacementPolicy_(replacementPolicy) { + + assert(blockSize_ > 0 && "Block size must be > 0."); + assert(setSize_ > 0 && "Set size must be > 0."); + assert(cacheSize_ > 0 && "Cache size must be > 0."); + +} + +/** + * Construct the object from ObjectState. + */ +CacheImplementation::CacheImplementation(const ObjectState* state) + + : CacheImplementation() { + loadState(state); +} + +CacheImplementation::~CacheImplementation() { +} +/** + * Return size of the blocks that is measured in number of "words". + */ +unsigned +CacheImplementation::blockSize() const { + return blockSize_; +} + +/** + * Set size of the blocks that is measured in number of "words". + * + * The block is synonumous with line or entry. Argument must be > 0; + */ +void +CacheImplementation::setBlockSize(unsigned size) { + blockSize_ = size; + assert(blockSize_ > 0 && "Block size must be > 0."); +} + +/** + * Returns size of the cache's set in number of blocks. + * + * The set is synonymous with way associativity. Argument must be > 0; + */ +unsigned +CacheImplementation::setSize() const { + return setSize_; +} + +/** + * Sets size of the cache's set in number of blocks. + * + * The set is equivalent to way associativity. Argument must be > 0; + */ +void +CacheImplementation::setSetSize(unsigned size) { + setSize_ = size; + assert(setSize_ > 0 && "Set size must be > 0."); +} + +/** + * Returns size of the cache in number of sets (or rows). + * + */ +unsigned +CacheImplementation::cacheSize() const { + return cacheSize_; +} + +/** + * Sets size of the cache in number of sets (or rows). + * + * Argument must be > 0; + */ +void +CacheImplementation::setCacheSize(unsigned size) { + cacheSize_ = size; + assert(cacheSize_ > 0 && "Cache size must be > 0."); +} + +/** + * Returns replacement policy. + */ +CacheImplementation::ReplacementPolicy +CacheImplementation::replacementPolicy() const { + return replacementPolicy_; +} + +/** + * Sets replacement policy. + */ +void +CacheImplementation::setReplacementPolicy( + CacheImplementation::ReplacementPolicy policy) { + replacementPolicy_ = policy; +} + +void +CacheImplementation::loadState(const ObjectState* state) { + if (state->name() != OSNAME_CACHE_IMPLEMENTATION) { + THROW_EXCEPTION(ObjectStateLoadingException, "Invalid object state."); + } + + try { + blockSize_ = state->unsignedIntAttribute(OSKEY_BLOCK_SIZE); + setSize_ = state->unsignedIntAttribute(OSKEY_SET_SIZE); + cacheSize_ = state->unsignedIntAttribute(OSKEY_CACHE_SIZE); + if (state->hasAttribute(OSKEY_REPLACEMENT_POLICY)) { + replacementPolicy_ = unserializeReplacementPolicy( + state->stringAttribute(OSKEY_REPLACEMENT_POLICY)); + } else { + replacementPolicy_ = ReplacementPolicy::INFERRED; + } + + } catch (const Exception& exception) { + THROW_EXCEPTION(ObjectStateLoadingException, exception.errorMessage()); + } +} + +ObjectState* +CacheImplementation::saveState() const { + ObjectState* state = new ObjectState(OSNAME_CACHE_IMPLEMENTATION); + state->setAttribute(OSKEY_BLOCK_SIZE, blockSize()); + state->setAttribute(OSKEY_SET_SIZE, setSize()); + state->setAttribute(OSKEY_CACHE_SIZE, cacheSize()); + if (replacementPolicy_ != ReplacementPolicy::INFERRED) { + state->setAttribute( + OSKEY_REPLACEMENT_POLICY, serialize(replacementPolicy())); + } + return state; +} + +/** + * Converts replacement policy enumeration into corresponding string. + */ +std::string +CacheImplementation::serialize( + ReplacementPolicy policy) { + + return replacementPolicyToString.at(policy); +} + +/** + * Converts serialized replacement policy into replacement policy enumeration. + */ +CacheImplementation::ReplacementPolicy +CacheImplementation::unserializeReplacementPolicy( + const std::string& serializedPolicy) { + if (stringToReplacementPolicy.count(serializedPolicy) == 0) { + std::string msg = std::string("Unknown replacement policy \"") + + serializedPolicy + "\"."; + THROW_EXCEPTION(NoKnownConversion, msg); + } else { + return stringToReplacementPolicy.at(serializedPolicy); + } +} + +} // namespace IDF diff --git a/openasip/src/base/idf/CacheImplementation.hh b/openasip/src/base/idf/CacheImplementation.hh new file mode 100644 index 0000000000..0c3bc1a0bb --- /dev/null +++ b/openasip/src/base/idf/CacheImplementation.hh @@ -0,0 +1,112 @@ +/* + Copyright (c) 2002-2015 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. + */ +/** + * @file CacheImplementation.hh + * + * Declaration of CacheImplementation. + * + * @author Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + + +#ifndef CACHEIMPLEMENTATION_HH +#define CACHEIMPLEMENTATION_HH + +#include + +#include "Serializable.hh" +#include "Exception.hh" + +namespace IDF { + +/** + * Represents implementation parameters for caches. + */ +class CacheImplementation : public Serializable { +public: + + enum class ReplacementPolicy { + /// Policy is not defined and is instead selected by application + INFERRED, + /// Block is evicted randomly. + RANDOM, + /// Least recently used block is evicted. + LRU + }; + + CacheImplementation(); + CacheImplementation( + unsigned blockSize, + unsigned setSize, + unsigned cacheSize, + ReplacementPolicy replacementPolicy); + CacheImplementation(const ObjectState* state); + virtual ~CacheImplementation(); + + unsigned blockSize() const; + void setBlockSize(unsigned size); + unsigned setSize() const; + void setSetSize(unsigned size); + unsigned cacheSize() const; + void setCacheSize(unsigned size); + ReplacementPolicy replacementPolicy() const; + void setReplacementPolicy(ReplacementPolicy policy); + + void loadState(const ObjectState* state) override; + + ObjectState* saveState() const override; + + /// ObjectState name for cache implementation. + static const std::string OSNAME_CACHE_IMPLEMENTATION; + /// ObjectState for cache block size. + static const std::string OSKEY_BLOCK_SIZE; + /// ObjectState for cache set size. + static const std::string OSKEY_SET_SIZE; + /// ObjectState for cache size. + static const std::string OSKEY_CACHE_SIZE; + /// ObjectState for replacement policy. + static const std::string OSKEY_REPLACEMENT_POLICY; + +private: + + static std::string serialize(ReplacementPolicy policy); + static ReplacementPolicy unserializeReplacementPolicy( + const std::string& serializedPolicy); + + /// The Size of a block/entry/line in words. + unsigned blockSize_; + /// The Size of a set of in blocks. + unsigned setSize_; + /// The number of sets in the cache. + unsigned cacheSize_; + /// The replacement policy. + ReplacementPolicy replacementPolicy_; + +}; + +} // namespace IDF + +#endif /* CACHEIMPLEMENTATION_HH */ + diff --git a/openasip/src/base/idf/MachineImplementation.cc b/openasip/src/base/idf/MachineImplementation.cc index e6022805c9..8768cf73bf 100644 --- a/openasip/src/base/idf/MachineImplementation.cc +++ b/openasip/src/base/idf/MachineImplementation.cc @@ -40,6 +40,9 @@ #include "IDFSerializer.hh" #include "ObjectState.hh" +#include "CacheImplementation.hh" +#include "ArbiterImplementation.hh" + using std::string; using std::vector; @@ -76,7 +79,10 @@ const std::string MachineImplementation::OSNAME_BUS_IMPLEMENTATIONS = "bus_impls"; const std::string MachineImplementation::OSNAME_SOCKET_IMPLEMENTATIONS = "socket_impls"; - +const std::string MachineImplementation::OSNAME_L1_ICACHE_IMPLEMENTATION = + "l1_icache_implementation"; +const std::string MachineImplementation::OSNAME_INSTRUCTION_ARBITER = + "instruction-arbiter"; /** * The constructor. @@ -230,7 +236,6 @@ MachineImplementation::hasDecompressorFile() const { bool MachineImplementation::hasFUImplementation( const std::string& unitName) const { - return findImplementation(fuImplementations_, unitName) != NULL; } @@ -768,6 +773,16 @@ MachineImplementation::loadState(const ObjectState* state) { clearState(); + if (state->hasChild(OSNAME_INSTRUCTION_ARBITER)) { + instructionArbiter_ = new ArbiterImplementation( + state->childByName(OSNAME_INSTRUCTION_ARBITER)); + } + + if (state->hasChild(OSNAME_L1_ICACHE_IMPLEMENTATION)) { + level1InstructionCache_ = new CacheImplementation( + state->childByName(OSNAME_L1_ICACHE_IMPLEMENTATION) + ->childByName(CacheImplementation::OSNAME_CACHE_IMPLEMENTATION)); + } if (state->hasChild(OSNAME_IC_DECODER_PLUGIN)) { ObjectState* icdecState = state->childByName(OSNAME_IC_DECODER_PLUGIN); @@ -864,6 +879,18 @@ MachineImplementation::saveState() const { ObjectState* state = new ObjectState(OSNAME_MACHINE_IMPLEMENTATION); state->setAttribute(OSKEY_SOURCE_IDF, sourceIDF_); + if (hasInstructionArbiter()) { + state->addChild(instructionArbiter().saveState()); + } + + + if (hasL1InstructionCache()) { + ObjectState* l1icacheState = new ObjectState( + OSNAME_L1_ICACHE_IMPLEMENTATION); + l1icacheState->addChild(l1InstructionCache().saveState()); + state->addChild(l1icacheState); + } + // add ic&decoder data if (hasICDecoderPluginName()) { ObjectState* icdecState = new ObjectState(OSNAME_IC_DECODER_PLUGIN); @@ -1528,6 +1555,41 @@ MachineImplementation::loadFromIDF(const std::string& idfFileName) { return serializer.readMachineImplementation(); } +/** + * Returns true if level 1 instruction cache is defined, otherwise false. + */ +bool +MachineImplementation::hasL1InstructionCache() const { + return level1InstructionCache_ != nullptr; +} + +/** + * Sets L1 instruction cache implementation. + */ +void +MachineImplementation::setL1InstructionCache(CacheImplementation* params) { + if (hasL1InstructionCache()) { + delete level1InstructionCache_; + level1InstructionCache_ = nullptr; + } + level1InstructionCache_ = params; +} + +/** + * Returns cache parameters for level 1 instruction cache. + * @exception InstanceNotFound Thrown if the cache implementation is not + * defined. + */ +CacheImplementation& +MachineImplementation::l1InstructionCache() const { + if (!hasL1InstructionCache()) { + std::string msg("No cache parameters for L1 instruction cache were " + "not defined."); + THROW_EXCEPTION(InstanceNotFound, msg); + } + return *level1InstructionCache_; +} + /** * Returns true if the given file is a library file of TCE. * @@ -1565,6 +1627,47 @@ MachineImplementation::isLibraryImplFile( return false; } +/** + * Returns true is user defined instruction bus arbiter is present. + */ +bool +MachineImplementation::hasInstructionArbiter() const { + return instructionArbiter_ != nullptr; +} + +/** + * Sets current instruction arbiter implementation. + */ +void +MachineImplementation::setInstructionArbiter(ArbiterImplementation* arbImpl) { + if (instructionArbiter_ != nullptr) { + delete instructionArbiter_; + instructionArbiter_ = nullptr; + } + instructionArbiter_ = arbImpl; +} + +/** + * Return implementation details of instruction arbiter. + */ +ArbiterImplementation& +MachineImplementation::instructionArbiter() const { + assert(instructionArbiter_ != nullptr); + return *instructionArbiter_; +} + +/** + * Returns true if simulation exit logic is set to be generated. + */ +bool +MachineImplementation::hasSimulationExitLogic() const { + if (icDecoderParameterValue("simulation-exit-logic") == "yes") { + return true; + } + return false; +} + + /** * Return all FUs to generate. */ @@ -1596,6 +1699,21 @@ MachineImplementation::hasFUGeneration(const std::string& name) const { return false; } +/** + * Return true if RF is to be generated. + * + * @param name Name of the RF Generation to check. + */ +bool +MachineImplementation::hasRFGeneration(const std::string& name) const { + for (const auto rfg : RFGenerated_) { + if (rfg.name() == name) { + return true; + } + } + return false; +} + /** * Remove fu from generation list. * @@ -1619,5 +1737,31 @@ void MachineImplementation::addFuGeneration(const FUGenerated& fug) { fuGenerated_.emplace_back(fug); } + +/** + * Return all RFs to generate. + */ +const std::vector& +MachineImplementation::RFGenerations() const { + return RFGenerated_; +} + +/** + * Return all RFs to generate. + */ +std::vector& +MachineImplementation::RFGenerations() { + return RFGenerated_; +} + +/** + * Add RF to generation list. + * + * @param rfg RF Generation to add. + */ +void +MachineImplementation::addRFGeneration(const RFGenerated& rfg) { + RFGenerated_.emplace_back(rfg); +} } diff --git a/openasip/src/base/idf/MachineImplementation.hh b/openasip/src/base/idf/MachineImplementation.hh index 500999d97b..8744a1aa20 100644 --- a/openasip/src/base/idf/MachineImplementation.hh +++ b/openasip/src/base/idf/MachineImplementation.hh @@ -41,12 +41,14 @@ #include "BusImplementationLocation.hh" #include "SocketImplementationLocation.hh" #include "FUGenerated.hh" - +#include "RFGenerated.hh" namespace IDF { class UnitImplementationLocation; +class ArbiterImplementation; +class CacheImplementation; /** * Represents the implementation of a machine defined in an IDF. @@ -96,6 +98,16 @@ public: int busImplementationCount() const; int socketImplementationCount() const; + bool hasL1InstructionCache() const; + void setL1InstructionCache(CacheImplementation* cacheImpl); + CacheImplementation& l1InstructionCache() const; + + bool hasInstructionArbiter() const; + void setInstructionArbiter(ArbiterImplementation* arbImpl); + ArbiterImplementation& instructionArbiter() const; + + bool hasSimulationExitLogic() const; + FUImplementationLocation& fuImplementation(const std::string& fu) const; RFImplementationLocation& rfImplementation(const std::string& rf) const; RFImplementationLocation& iuImplementation(const std::string& iu) const; @@ -167,12 +179,22 @@ public: static const std::string OSNAME_BUS_IMPLEMENTATIONS; /// ObjectState name for socket implementation container. static const std::string OSNAME_SOCKET_IMPLEMENTATIONS; + /// ObjectState name for L1 instruction cache parameters. + static const std::string OSNAME_L1_ICACHE_IMPLEMENTATION; + /// ObjectState name for instruction interface specification. + static const std::string OSNAME_INSTRUCTION_ARBITER; const std::vector& FUGenerations() const; std::vector& FUGenerations(); bool hasFUGeneration(const std::string& name) const; void removeFuGeneration(const std::string& name); void addFuGeneration(const FUGenerated& fug); + const std::vector& RFGenerations() const; + std::vector& RFGenerations(); + bool hasRFGeneration(const std::string& name) const; + void removeRFGeneration(const std::string& name); + void addRFGeneration(const RFGenerated& rfg); + private: /// Vector type for UnitImplementationLocation. @@ -200,6 +222,8 @@ private: /// Generated FUs. std::vector fuGenerated_; + /// Generated RFs. + std::vector RFGenerated_; /// FU implementations. ImplementationTable fuImplementations_; @@ -230,6 +254,10 @@ private: /// Possible alternative file paths for missing implementation files. std::vector alternativeFiles_; + /// L1 instruction cache implementation. + CacheImplementation* level1InstructionCache_ = nullptr; + /// Instruction Bus arbiter implementation. + ArbiterImplementation* instructionArbiter_ = nullptr; }; } diff --git a/openasip/src/base/idf/Makefile.am b/openasip/src/base/idf/Makefile.am index 65e477984d..37cc1382b8 100644 --- a/openasip/src/base/idf/Makefile.am +++ b/openasip/src/base/idf/Makefile.am @@ -1,7 +1,8 @@ noinst_LTLIBRARIES = libidf.la libidf_la_SOURCES = MachineImplementation.cc UnitImplementationLocation.cc \ -IDFSerializer.cc NullUnitImplementationLocation.cc FUGenerated.cc +IDFSerializer.cc NullUnitImplementationLocation.cc CacheImplementation.cc \ +ArbiterImplementation.cc FUGenerated.cc RFGenerated.cc libidf_la_LDFLAGS = $PTHREAD_LIBS @@ -28,5 +29,6 @@ libidf_la_SOURCES += \ SocketImplementationLocation.hh RFImplementationLocation.hh \ MachineImplementation.hh BusImplementationLocation.hh \ NullUnitImplementationLocation.hh IDFSerializer.hh \ - FUGenerated.hh + CacheImplementation.hh ArbiterImplementation.hh \ + FUGenerated.hh RFGenerated.hh ## headers end diff --git a/openasip/src/base/idf/RFGenerated.cc b/openasip/src/base/idf/RFGenerated.cc new file mode 100644 index 0000000000..77769a36dc --- /dev/null +++ b/openasip/src/base/idf/RFGenerated.cc @@ -0,0 +1,78 @@ +/* + Copyright (C) 2024 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 RFGenerated.cc + * + * Implementation of RFGenerated. + * + * @author Joonas Multanen 2024 (joonas.multanen-no-spam-tuni.fi) + * @note rating: red +*/ + +#include "RFGenerated.hh" +#include "MachineImplementation.hh" + +namespace IDF { + +const std::string ATTRIB_NAME = "name"; +const std::string TAG_OPTION = "option"; +const std::string TAG_RFGENERATE = "rf-generate"; + +RFGenerated::RFGenerated(const std::string& name) : name_(name) {} + +void +RFGenerated::loadState(const ObjectState* state) { + name_ = state->stringAttribute(ATTRIB_NAME); + + for (int i = state->childCount() - 1; i >= 0; --i) { + ObjectState* child = state->child(i); + options_.emplace_back(child->stringValue()); + } +} + +ObjectState* +RFGenerated::saveState() const { + ObjectState* state = new ObjectState(TAG_RFGENERATE); + + state->setAttribute(ATTRIB_NAME, name_); + + for (const auto option : options_) { + ObjectState* opState = new ObjectState(TAG_OPTION); + opState->setValue(option); + state->addChild(opState); + } + + return state; +} + +std::string +RFGenerated::name() const { + return name_; +} + +void +RFGenerated::name(const std::string& newName) { + name_ = newName; +} + +const std::vector& +RFGenerated::options() const { + return options_; +} +} // namespace IDF diff --git a/openasip/src/base/idf/RFGenerated.hh b/openasip/src/base/idf/RFGenerated.hh new file mode 100755 index 0000000000..db7dbe49f5 --- /dev/null +++ b/openasip/src/base/idf/RFGenerated.hh @@ -0,0 +1,63 @@ +/* + Copyright (c) 2002-2024 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. + */ +/** + * @file RFGenerated.hh + * + * Declaration of RFGenerated. + * + * @author Joonas Multanen 2024 (joonas.multanen-no.spam-tuni.fi) + */ +#pragma once + +#include "ObjectState.hh" +#include "Serializable.hh" + +#include +#include + +namespace IDF { + + class RFGenerated : public Serializable { + public: + struct Info { + int id; + int latency; + }; + + RFGenerated() = default; + RFGenerated(const std::string& name); + virtual ~RFGenerated() = default; + + void loadState(const ObjectState* state) override; + ObjectState* saveState() const override; + + std::string name() const; + void name(const std::string& newName); + const std::vector& options() const; + + private: + std::string name_; + std::vector options_; + }; +} diff --git a/openasip/src/base/osal/Operation.cc b/openasip/src/base/osal/Operation.cc index 9ad54716a2..730f59e91f 100644 --- a/openasip/src/base/osal/Operation.cc +++ b/openasip/src/base/osal/Operation.cc @@ -35,6 +35,7 @@ #include +#include "RandomNumberGenerator.hh" #include "Operation.hh" #include "OperationPimpl.hh" #include "TCEString.hh" @@ -325,7 +326,11 @@ Operation::isBaseOffsetMemOperation() const { return upperName == "ALDW" || upperName == "ALDHU" || upperName == "ALDH" || upperName == "ALDQU" || upperName == "ALDQ" || upperName == "ASTW" || - upperName == "ASTH" || upperName == "ASTQ"; + upperName == "ASTH" || upperName == "ASTQ" || + upperName == "ALD8" || upperName == "ALDU8" || + upperName == "ALD16" || upperName == "ALDU16" || + upperName == "ALD32" || upperName == "AST32" || + upperName == "AST16" || upperName == "AST8"; } /** @@ -580,6 +585,51 @@ Operation::areValid( && pimpl_->behavior().areValid(inputs, context); } +/** + * Returns set of test vectors for the operation. + * + * Base implementation returns random set of test vectors. Custom operations + * may override this and return their own defined test vectors. + * + * @param testVectorsOut The container where new test vectors are added to. + * @param seed The seed value for randomized test vector generation. Same seed + * used in subsequent calls produces same set of test vectors. + * @param context The operation context for test vector generation. + */ +void +Operation::makeTestVectors( + std::vector& testVectorsOut, + RandomNumberGenerator::SeedType seed, + const OperationContext& context, + unsigned numberOfVector) const { + + if (!canBeSimulated()) { + return; + } + + pimpl_->behavior().makeTestVectors( + testVectorsOut, seed, context, numberOfVector); +} + +/** + * Returns set of random test vectors suitable for basic operations. + * + * Base implementation returns random set of test vectors. Custom operations + * may override this and return their own defined test vectors. + * + * @param testVectorsOut The container where new test vectors are added to. + * @param seed The seed value for randomized test vector generation. + */ +void +Operation::makeTestVectors( + std::vector& testVectorsOut, + RandomNumberGenerator::SeedType seed, + unsigned numberOfVector) const { + + OperationContext opContext; + makeTestVectors(testVectorsOut, seed, opContext, numberOfVector); +} + /** * Creates an instance of operation state for this operation and adds it to * the operation context. diff --git a/openasip/src/base/osal/Operation.hh b/openasip/src/base/osal/Operation.hh index bce0348bd0..3365f85137 100644 --- a/openasip/src/base/osal/Operation.hh +++ b/openasip/src/base/osal/Operation.hh @@ -157,6 +157,15 @@ public: virtual bool areValid( const InputOperandVector& inputs, const OperationContext& context) const; + virtual void makeTestVectors( + std::vector& testVectorsOut, + ::RandomNumberGenerator::SeedType seed, + const OperationContext& context, + unsigned numberOfVector = 20) const; + virtual void makeTestVectors( + std::vector& testVectorsOut, + ::RandomNumberGenerator::SeedType seed, + unsigned numberOfVector = 20) const; virtual void createState(OperationContext& context) const; virtual void deleteState(OperationContext& context) const; diff --git a/openasip/src/base/osal/OperationBehavior.cc b/openasip/src/base/osal/OperationBehavior.cc index d1efb83ecb..ce0d74bf05 100644 --- a/openasip/src/base/osal/OperationBehavior.cc +++ b/openasip/src/base/osal/OperationBehavior.cc @@ -42,6 +42,7 @@ #include "OperationGlobals.hh" #include "Operation.hh" #include "Operand.hh" +#include "RandomNumberGenerator.hh" using std::string; @@ -100,6 +101,71 @@ OperationBehavior::areValid( return true; } +/** + * The default implementation of test vector generation. + * + * Generates set of test vectors with randomized values. Calls areValid() + * for each generated test vector to check and discard invalid vectors. + * New test vectors are appended to the testVectorsOut. + * + * @param testVectorsOut The container where new test vectors are added to. + * @param seed The seed value for randomized test vector generation. + * @param context The operation context for test vector generation. + * @param numberOfVectors The number of test vectors attempted to generate. + */ +void +OperationBehavior::makeTestVectors( + std::vector& testVectorsOut, + RandomNumberGenerator::SeedType seed, + const OperationContext& context, + unsigned numberOfVectors) const { + + if (parent_.isNull()) { + return; + } + + const size_t nVectorsAlrearyIn = testVectorsOut.size(); + const size_t nVectorsToGenerate = numberOfVectors + nVectorsAlrearyIn; + const size_t attempts = 30 + nVectorsToGenerate; + RandomNumberGenerator throwDice(seed); + + for (size_t itv = 0; itv < attempts; itv++) { + testVectorsOut.push_back(InputOperandVector(parent_.numberOfInputs())); + InputOperandVector& operands = testVectorsOut.back(); + for (int iop = 0; iop < parent_.numberOfInputs(); iop++) { + for (int ielem = 0; + ielem < parent_.input(iop).elementCount(); + ielem++) { + Operand::OperandType type = parent_.input(iop).type(); + if (type == Operand::FLOAT_WORD) { + assert(parent_.input(iop).elementWidth() == 32); + operands.at(iop).setFloatElement( + ielem, RandomNumberGenerator::asFloat(throwDice())); + } else if (type == Operand::HALF_FLOAT_WORD) { + assert(parent_.input(iop).elementWidth() == 16); + operands.at(iop).setHalfFloatElement( + ielem, + RandomNumberGenerator::asHalfFloat(throwDice())); + } else if (type == Operand::DOUBLE_WORD) { + assert(parent_.input(iop).elementWidth() == 64); + operands.at(iop).setDoubleFloatElement( + ielem, RandomDoubleGenerator(throwDice())()); + } else { + operands.at(iop).setElement( + ielem, parent_.input(iop).elementWidth(), throwDice()); + } + } + } + assert(testVectorsOut.back().size() == + static_cast(parent_.numberOfInputs())); + if (!areValid(testVectorsOut.back(), context)) { + testVectorsOut.pop_back(); // Discard invalid vector + } + if (testVectorsOut.size() >= nVectorsToGenerate ) { + break; + } + } +} /** * Writes text to the output stream specified diff --git a/openasip/src/base/osal/OperationBehavior.hh b/openasip/src/base/osal/OperationBehavior.hh index e59a2ec362..f2c7287f20 100644 --- a/openasip/src/base/osal/OperationBehavior.hh +++ b/openasip/src/base/osal/OperationBehavior.hh @@ -38,6 +38,7 @@ #include #include "SimValue.hh" +#include "RandomNumberGenerator.hh" class OperationContext; class Operation; @@ -62,6 +63,11 @@ public: virtual bool areValid( const InputOperandVector& inputs, const OperationContext& context) const; + virtual void makeTestVectors( + std::vector& testVectorsOut, + ::RandomNumberGenerator::SeedType seed, + const OperationContext& context, + unsigned numberOfVectors = 20) const; virtual void createState(OperationContext& context) const; virtual void deleteState(OperationContext& context) const; diff --git a/openasip/src/bintools/Makefile.am b/openasip/src/bintools/Makefile.am index 942ef33305..8b469e8958 100644 --- a/openasip/src/bintools/Makefile.am +++ b/openasip/src/bintools/Makefile.am @@ -1,5 +1,6 @@ SUBDIRS = DictionaryTool TPEFDumper PIG BEMGenerator Assembler \ - BEMViewer Disassembler BlocksTranslator BlocksDisassembler + BEMViewer Disassembler \ + TestGenerator BlocksTranslator BlocksDisassembler if LLVM SUBDIRS += Compiler diff --git a/openasip/src/bintools/TestGenerator/GenerateTests.cc b/openasip/src/bintools/TestGenerator/GenerateTests.cc new file mode 100644 index 0000000000..c730d79e21 --- /dev/null +++ b/openasip/src/bintools/TestGenerator/GenerateTests.cc @@ -0,0 +1,385 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file GenerateTests.cc + * + * + * + * Created on: 24.2.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include +#include +#include +#include +#include + +#include "Application.hh" +#include "TestGenCmdLineOptions.hh" +#include "FileSystem.hh" +#include "Machine.hh" +#include "Program.hh" +#include "ProgramWriter.hh" +#include "TPEFWriter.hh" +#include "BinaryStream.hh" +#include "TestCase.hh" +#include "VerificationDataGenerator.hh" +#include "TestFileWriter.hh" +#include "Exception.hh" +#include "MachineImplementation.hh" + +#include "TestGeneratorCollection.hh" +#include "TestGeneratorBase.hh" + +using std::cout; +using std::cerr; +using std::endl; +using namespace TTAProgram; +using namespace TTAMachine; + +template +void loadPrograms( + const std::set& sourceFilePaths, + const TTAMachine::Machine& targetArch, + ContainerType& programContainer); +bool getOutputDirectory(const TestGenCmdLineOptions& options, Path& outputDir); +void writeTPEF(const TestCase& testCase, Path& outputDir); +void printGeneratorList(const TestGeneratorCollection& generators); +RandomNumberGenerator::SeedType getSeed(const TestGenCmdLineOptions& options); + +/** + * The main function. + */ +int main(int argc, char* argv[]) { + + TestGeneratorCollection testGenerators; + TestGenCmdLineOptions options; + try { + options.parse(argv, argc); + if (options.listGeneratorsDefined()) { + printGeneratorList(testGenerators); + return EXIT_SUCCESS; + } + options.validate(); + } catch (const ParserStopRequest&) { + return EXIT_SUCCESS; + } catch (const IllegalCommandLine& exception) { + cerr << exception.errorMessage() << endl; + return EXIT_FAILURE; + } + + if (options.isVerboseSpamSwitchDefined()) { + Application::setVerboseLevel(2); + } else if (options.isVerboseSwitchDefined()) { + Application::setVerboseLevel(1); + } else { + Application::setVerboseLevel(); + } + + // Initialize test generators // + const Machine& targetMachine = + *Machine::loadFromADF(options.adfFilePath()); + testGenerators.setMachine(targetMachine); + const IDF::MachineImplementation* targetImpl = nullptr; + if (options.idfDefined()) { + targetImpl = IDF::MachineImplementation::loadFromIDF( + options.idfFilePath().string()); + testGenerators.setImplementation(*targetImpl); + } + //todo Do enabling/disabling in order of arguments i.e. allow + // "-D -e some-test" => disable all first then enable some-test. + if (options.disableAllTestGenerators()) { + for (auto& generator : testGenerators.getGenerators()) { + testGenerators.setEnabled(generator.second->title(), false); + } + } + + for (size_t i = 0; i < options.toDisabledListSize(); i++) { + testGenerators.setEnabled(options.toDisabled(i), false); + } + for (size_t i = 0; i < options.toEnabledListSize(); i++) { + testGenerators.setEnabled(options.toEnabled(i)); + } + + std::set userSourceFiles = options.programFiles(); + std::set::const_iterator prog_it; + + // Load user defined test programs // + std::vector userTestCases; + loadPrograms(userSourceFiles, targetMachine, userTestCases); + + Path outputDir(""); + if (!getOutputDirectory(options, outputDir)) { + return EXIT_FAILURE; + } + + if (Application::spamVerbose()) { + cout << "Loaded user test code(s): "; + std::vector::const_iterator tc_it; + for (tc_it = userTestCases.begin(); tc_it != userTestCases.end(); + tc_it++) { + cout << tc_it->testName() << ", "; + } + if (userTestCases.empty()) { + cout << "none"; + } + cout << endl; + } + + TestFileWriter testFileWriter; + testFileWriter.setOutput(outputDir); + testFileWriter.setMachine(targetMachine); + testFileWriter.setImageFormat(options.programImageFormat()); + testFileWriter.setDataMemoryWidthInMAUs(options.dataMemoryWidthInMAUs()); + + // Call Test Generators to produce test programs // + RandomNumberGenerator::SeedType seed = getSeed(options); + std::vector testcases; + testcases = testGenerators.generateTestCases(seed); + if (Application::increasedVerbose()) { + cout << "Generated test case count: " + << Conversion::toString(testcases.size()) << endl; + } + + // Write ouput files // + std::vector::const_iterator tc_it; + for (tc_it = testcases.begin(); + tc_it != testcases.end(); tc_it++) { + testFileWriter.setTestCase(*tc_it); + if (tc_it->generateVerificationData()) { + try { + testFileWriter.makeVerificationData(); + } catch (Exception& e) { + if (Application::increasedVerbose()) { + std::cerr << "Error from verification data generator. " + << "Message: " << endl << e.errorMessage() << endl; + } + continue; + } + } + try { + testFileWriter.makeTPEF(); + testFileWriter.makeDisassembly(); + testFileWriter.makeProgramImages(); + } catch (Exception& e) { + if (Application::increasedVerbose()) { + std::cerr << "Error from test file writer." + << "Message: " << endl << e.errorMessage() << endl; + } + continue; + } + } + + for (tc_it = userTestCases.begin(); + tc_it != userTestCases.end(); tc_it++) { + testFileWriter.setTestCase(*tc_it); + if (tc_it->generateVerificationData()) { + try { + testFileWriter.makeVerificationData(); + } catch (IOException& e) { + if (Application::increasedVerbose()) { + std::cerr << "Error from verification data generator. " + << "Message: " << endl << e.errorMessage() << endl; + } + continue; + } + } + testFileWriter.makeTPEF(); + testFileWriter.makeDisassembly(); + testFileWriter.makeProgramImages(); + } + + if (options.progeOutputDirDefined()) { + testFileWriter.makeTestRunnerScript(options.progeOutputDir()); + } else { + testFileWriter.makeTestRunnerScript(outputDir); + } + + return EXIT_SUCCESS; +} + +/** + * Loads valid code files it recognizes by file extension as TestCases. + * + * @param sourceFilePaths The source files. + * @param targetArch The target machine. + * @param programInserter The container in which TestCases are inserted. The + * container must have STL-style insert()-function. + */ +template +void loadPrograms( + const std::set& sourceFilePaths, + const TTAMachine::Machine& targetArch, + ContainerType& programContainer) { + + std::insert_iterator programInserter = + std::inserter(programContainer, programContainer.end()); + + std::set::const_iterator src_it; + for (src_it = sourceFilePaths.begin(); src_it != sourceFilePaths.end(); + src_it++) { + + if (!FileSystem::fileExists(*src_it)) { + if (Application::increasedVerbose()) { + cout << "Skipped non-existent file: " << src_it->string() + << endl; + } + continue; + } + + if (src_it->extension() == ".tpef") { + Program* prog = NULL; + try { + prog = Program::loadFromTPEF(*src_it, targetArch); + } catch (Exception& e) { + if (Application::increasedVerbose()) { + cout << "Skipped invalid TPEF file. " + << "Loader message: " << endl << e.errorMessage() + << endl; + } + continue; + } + TestCase testcase(prog, src_it->stem().string()); + programInserter = testcase; + + } else { + // Otherwise ignore unresolved file. + if (Application::increasedVerbose()) { + cerr << "Skipped unresolved user code: " + << src_it->string() << endl; + } + } + } +} + +/** + * Resolves target directory for the generated tests. + * + * @param options Queries output options. + * @param outputDirOut The target directory + * @return True, if resolution was successful. Otherwise returns false. + */ +bool +getOutputDirectory(const TestGenCmdLineOptions& options, Path& outputDirOut) { + if (options.progeOutputDirDefined()) { + outputDirOut /= options.progeOutputDir(); + if (!FileSystem::fileExists(outputDirOut)) { + std::cerr << "Error: Could not found proge output directory " + << outputDirOut.string() + << endl; + return false; + } + outputDirOut /= "tests"; + } else if (options.outputDirDefined()) { + outputDirOut /= options.outputDir(); + } else { + outputDirOut /= "tests"; + } + if (!FileSystem::isAbsolutePath(outputDirOut)) { + outputDirOut = Path(FileSystem::currentWorkingDir()) /= outputDirOut; + } + if (!FileSystem::fileExists(outputDirOut)) { + assert(FileSystem::createDirectory(outputDirOut)); + } + if (Application::increasedVerbose()) { + cerr << "Output directory set to: " + << "[" << outputDirOut.string() << "]" << endl; + } + assert(FileSystem::fileExists(outputDirOut)); + + return true; +} + +/** + * For debugging. + */ +void +writeTPEF(const TestCase& testCase, Path& outputDir) { + using namespace TPEF; + Path tpefFile = outputDir; + tpefFile /= testCase.testName() + ".tpef"; + ProgramWriter progWriter(*testCase.program()); + BinaryStream tpefStream(tpefFile); + TPEFWriter::instance().writeBinary(tpefStream, progWriter.createBinary()); +} + + +void +printGeneratorList(const TestGeneratorCollection& collection) { + const std::string IDENT = " "; + + TestGeneratorCollection::GeneratorMap::const_iterator it; + const TestGeneratorCollection::GeneratorMap& testGenerators = + collection.getGenerators(); + + for (it = testGenerators.begin(); it != testGenerators.end(); it++) { + const TestGeneratorBase& gen = *(it->second); + cout << gen.title() << TCEString::applyIf(gen.isEnabled(), + " [enabled]", " [disabled]") << endl; + std::stringstream desc(gen.description()); + std::string word; + size_t charsInLine = IDENT.size(); + cout << IDENT; + while (desc >> word) { + if (charsInLine + word.size() > 79) { + cout << endl << IDENT << word << " "; + charsInLine = IDENT.size() + word.size() + 1; + + } else { + cout << word << " "; + charsInLine += word.size() + 1; + } + } + cout << endl; + } + cout << endl + << "Use -e COMMA-SEPARATED-LIST to enable test generators." << endl + << "Use -d COMMA-SEPARATED-LIST to disable test generators." << endl + << "COMMA-SEPARATED-LIST is list of test generators listed above." + << endl; +} + + +/** + * Returns seed value for random number generator. + */ +RandomNumberGenerator::RNGValueType +getSeed(const TestGenCmdLineOptions& options) { + if (options.seedDefined()) { + const std::string& seedString = options.getSeed(); + if (seedString.empty()) { + RandomNumberGenerator::RNGValueType newSeed = RandomNumberGenerator::generateNewSeed(); + cout << "Generated seed: " << newSeed << endl; + return newSeed; + } else { + return RandomNumberGenerator::convertToSeed(seedString); + } + } else { + return RandomNumberGenerator::DEFAULTSEED; + } +} + + diff --git a/openasip/src/bintools/TestGenerator/Makefile.am b/openasip/src/bintools/TestGenerator/Makefile.am new file mode 100644 index 0000000000..0cb065c309 --- /dev/null +++ b/openasip/src/bintools/TestGenerator/Makefile.am @@ -0,0 +1,37 @@ +PROJECT_ROOT = $(top_srcdir) +SRC_ROOT_DIR = ${PROJECT_ROOT}/src + +LIB_TCETOOLS_DIR = ../../tools +LIB_APPLIBS_DIR = ../../applibs +LIB_BASE_DIR = ../../base +ROOT_DIR = ../../.. + +TOOLS_DIR = ${SRC_ROOT_DIR}/tools +MACH_DIR = ${SRC_ROOT_DIR}/base/mach +IDF_DIR = ${SRC_ROOT_DIR}/base/idf +PROG_DIR = ${SRC_ROOT_DIR}/base/program +TPEF_DIR = ${SRC_ROOT_DIR}/base/tpef +TEGE_APPLIBS_DIR = ${SRC_ROOT_DIR}/applibs/TestGenerator +SIM_APPLIBS_DIR = ${SRC_ROOT_DIR}/applibs/Simulator +PIG_APPLIBS_DIR = ${SRC_ROOT_DIR}/applibs/PIG + +bin_PROGRAMS = generatetests +generatetests_SOURCES= GenerateTests.cc TestGenCmdLineOptions.cc + +generatetests_LDADD = ../../libopenasip.la + +AM_CPPFLAGS = -I${LIB_TCETOOLS_DIR} -I${ROOT_DIR} -I${LIB_BASE_DIR} \ + -I${MACH_DIR} -I${IDF_DIR} -I${TOOLS_DIR} -I${PROG_DIR} \ + -I${TEGE_APPLIBS_DIR} -I${SIM_APPLIBS_DIR} \ + -I${TPEF_DIR} -I${PIG_APPLIBS_DIR} +AM_CPPFLAGS += -I${PROJECT_ROOT} +AM_LDFLAGS = ${TCE_LDFLAGS} + +dist-hook: + rm -rf $(distdir)/CVS $(distdir)/.deps $(distdir)/Makefile + +MAINTAINERCLEANFILES = *~ *.gcov *.bbg *.bb *.da + +## headers start +generatetests_SOURCES += TestGenCmdLineOptions.hh +## headers end diff --git a/openasip/src/bintools/TestGenerator/TestGenCmdLineOptions.cc b/openasip/src/bintools/TestGenerator/TestGenCmdLineOptions.cc new file mode 100644 index 0000000000..6783d2e2dd --- /dev/null +++ b/openasip/src/bintools/TestGenerator/TestGenCmdLineOptions.cc @@ -0,0 +1,390 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestGenCmdLineOptions.cc + * + * Implementation of TestGenCmdLineOptions class. + * + * Created on: 24.2.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "TestGenCmdLineOptions.hh" + +#include "TCEString.hh" +#include "FileSystem.hh" + +const TCEString TestGenCmdLineOptions::PROGEOUTDIR_OPT = "hdl-dir"; +const TCEString TestGenCmdLineOptions::OUTPUTDIR_OPT = "output"; +const TCEString TestGenCmdLineOptions::PROGRAMS_OPT = "program"; +const TCEString TestGenCmdLineOptions::LIST_GENERATORS = "list-generators"; +const TCEString TestGenCmdLineOptions::ENABLE_LIST_OPT = "enable"; +const TCEString TestGenCmdLineOptions::DISABLE_LIST_OPT = "disable"; +const TCEString TestGenCmdLineOptions::DISABLE_ALL_OPT = "disable-all"; +const TCEString TestGenCmdLineOptions::SEED_OPT = "seed"; +const TCEString TestGenCmdLineOptions::ADF_OPT = "adf"; +const TCEString TestGenCmdLineOptions::IDF_OPT = "idf"; +const TCEString TestGenCmdLineOptions::IMAGE_FORMAT_OPT = "format"; +const TCEString TestGenCmdLineOptions::DMEM_WIDTH_IN_MAUS_OPT = + "dmemwidthinmaus"; + +/** + * Constructor. Adds command line parameters. + */ +TestGenCmdLineOptions::TestGenCmdLineOptions() + : CmdLineOptions("") { + + addOption(new StringCmdLineOptionParser( + ADF_OPT, + "The target architecture for tests. The option is Required. ", + "a")); + addOption(new StringCmdLineOptionParser( + IDF_OPT, + "The implementation definition for target architecture. The option is " + "optional but implicitly disables some test generators if left out", + "i")); + addOption(new BoolCmdLineOptionParser( + LIST_GENERATORS, + "Lists the test generators with their names, descriptions " + "and activation status.", + "l")); + addOption(new StringListCmdLineOptionParser( + ENABLE_LIST_OPT, + "Enables test generators. Format of LIST is " + "comma separated of names of test generators.", "e")); + addOption(new StringListCmdLineOptionParser( + DISABLE_LIST_OPT, + "Disables test generators. Format of LIST is " + "Format of LIST is comma separated of names of " + "test generators.", "d")); + addOption(new BoolCmdLineOptionParser( + DISABLE_ALL_OPT, + "Disables all test generators. This option can be chained with -e.", + "D")); + addOption(new OptionalStringCmdLineOptionParser( + SEED_OPT, + "Uses VALUE as seed for random number generator " + "to generate determined set of test cases. " + "If VALUE is empty the seed is generated randomly. " + "If option is not specified a default " + "seed value is used instead.", "s")); + addOption(new StringCmdLineOptionParser( + OUTPUTDIR_OPT, + "Additional output directory where assembly files of " + "generated tests are placed.", "o")); + addOption(new StringCmdLineOptionParser( + PROGEOUTDIR_OPT, + "Directory root where ProGe generated HDL files. " + "GenerateTests will place test files under its tb/ directory", "x")); + addOption(new StringListCmdLineOptionParser( + IMAGE_FORMAT_OPT, + "Output format of program and data images." + "Choices are 'ascii', 'array', 'mif', 'coe', 'vhdl' or 'binary'. " + "Default is 'ascii' ", "f")); + addOption(new StringListCmdLineOptionParser( + PROGRAMS_OPT, + "Additional test programs to be included in tests.", + "p")); + addOption(new BoolCmdLineOptionParser( + "help", "Print this text.", "h")); + addOption(new IntegerCmdLineOptionParser( + DMEM_WIDTH_IN_MAUS_OPT, + "Width of data memory in MAUs. Default is 1.", "w")); + +} + +/** + * Destructor. + */ +TestGenCmdLineOptions::~TestGenCmdLineOptions() { +} + + +/** + * Defines options rules for this class. + * + * Checks that given options are valid and required options are present. + * + * @exception IllegalCommandLine If there are invalid or missing options. + */ +void +TestGenCmdLineOptions::validate() const { + std::string msg; + bool allOk = true; + + if (!adfDefined()) { + allOk &= adfDefined(); + msg += "ADF is required. Use -a ADF option.\n"; + } + + //todo check for valid formats + + if (!allOk) { + throw IllegalCommandLine(__FILE__, __LINE__, + "TestGenCmdLineOptions::validate()", msg); + } +} + +/** + * Prints the version of the application. + */ +void TestGenCmdLineOptions::printVersion() const { + std::cout << "generatetests - Automated Test Generator Utility " + << Application::TCEVersionString() << std::endl; +} + +/** + * Returns true if ADF option is defined. + */ +bool +TestGenCmdLineOptions::adfDefined() const { + if (!optionGiven(ADF_OPT)) { + return false; + } + return true; +} + +/** + * Returns file path to the ADF. + */ +Path TestGenCmdLineOptions::adfFilePath() const { + assert(adfDefined()); + Path adfPath(findOption(ADF_OPT)->String(0)); + return adfPath; +} + +/** + * Returns true if IDF option is defined. + */ +bool +TestGenCmdLineOptions::idfDefined() const { + return optionGiven(IDF_OPT); +} + +/** + * Returns file path to the IDF. + */ +Path +TestGenCmdLineOptions::idfFilePath() const { + assert(idfDefined()); + Path idfPath(findOption(IDF_OPT)->String(0)); + return idfPath; +} + +/** + * Prints help of the application. + */ +void TestGenCmdLineOptions::printHelp() const { + printVersion(); + std::cout << "Usage: generatetests [options]... -a ADF"; + std::cout << std::endl; + CmdLineOptions::printHelp(); +} + +/** + * Returns the given width of data memory in MAUs. + * + * @return The width. + */ +int +TestGenCmdLineOptions::dataMemoryWidthInMAUs() const { + CmdLineOptionParser* option = findOption( + DMEM_WIDTH_IN_MAUS_OPT); + if (option->isDefined()) { + return option->integer(); + } else { + return 1; + } +} + +/** + * returns true if output directory option is defined (-o). + */ +bool +TestGenCmdLineOptions::outputDirDefined() const { + return optionGiven(OUTPUTDIR_OPT); +} + +/** + * returns output directory (-o). + */ +Path +TestGenCmdLineOptions::outputDir() const { + assert(outputDirDefined()); + return Path(findOption(OUTPUTDIR_OPT)->String(0)); +} + +/** + * Returns true if proge output directory option is defined (-x). + */ +bool +TestGenCmdLineOptions::progeOutputDirDefined() const { + return optionGiven(PROGEOUTDIR_OPT); +} + +/** + * Returns base directory path of ProGe output. + */ +Path +TestGenCmdLineOptions::progeOutputDir() const { + assert(progeOutputDirDefined()); + Path adfPath(findOption(PROGEOUTDIR_OPT)->String(0)); + return adfPath; +} + +/** + * Returns set of custom test program filepaths provided by user. + */ +std::set +TestGenCmdLineOptions::programFiles() const { + const CmdLineOptionParser& progOptParser = *findOption(PROGRAMS_OPT); + std::set progSet; + for (int i = 1; i <= progOptParser.listSize(); i++) { + Path progFile(progOptParser.String(i)); + if (progFile.has_filename()) { + progSet.insert(progFile); + } + } + return progSet; +} + +/** + * True if application should print all test generator descriptions. + */ +bool +TestGenCmdLineOptions::listGeneratorsDefined() const { + return optionGiven(LIST_GENERATORS); +} + +/** + * Item count of test generators to be enabled. + */ +size_t +TestGenCmdLineOptions::toEnabledListSize() const { + if (optionGiven(ENABLE_LIST_OPT)) { + return findOption(ENABLE_LIST_OPT)->listSize(); + } else { + return 0; + } +} + +/** + * Return name of test generator to be enabled at index + * in range [0, toEnabledListSize()]. + */ +std::string +TestGenCmdLineOptions::toEnabled(size_t index) const { + assert(index < toEnabledListSize()); + assert(optionGiven(ENABLE_LIST_OPT)); + return findOption(ENABLE_LIST_OPT)->String(index + 1); +} + +/** + * Item count of test generators to be disabled. + */ +size_t +TestGenCmdLineOptions::toDisabledListSize() const { + if (optionGiven(DISABLE_LIST_OPT)) { + return findOption(DISABLE_LIST_OPT)->listSize(); + } else { + return 0; + } +} + +/** + * Return name of test generator to be disabled at index + * in range [0, toDisabledListSize()]. + */ +std::string +TestGenCmdLineOptions::toDisabled(size_t index) const { + assert(index < toDisabledListSize()); + assert(optionGiven(DISABLE_LIST_OPT)); + return findOption(DISABLE_LIST_OPT)->String(index + 1); +} + +/** + * Return true if all test generators should be disabled. + */ +bool +TestGenCmdLineOptions::disableAllTestGenerators() const { + return optionGiven(DISABLE_ALL_OPT); +} + +/** + * True if user provides seed value. + */ +bool +TestGenCmdLineOptions::seedDefined() const { + return optionGiven(SEED_OPT); +} + +/** + * The given seed value that may be non-numerical. + */ +std::string +TestGenCmdLineOptions::getSeed() const { + return findOption(SEED_OPT)->String(0); +} + +/** + * Returns program image format. + */ +ProgramImageGenerator::OutputFormat +TestGenCmdLineOptions::programImageFormat() const { + if (!optionGiven(IMAGE_FORMAT_OPT)) { + return ProgramImageGenerator::ASCII; + } else { + const std::string format = findOption(IMAGE_FORMAT_OPT)->String(1); + if (format == "ascii") { + return ProgramImageGenerator::ASCII; + } else if (format == "array") { + return ProgramImageGenerator::ARRAY; + } else if (format == "mif") { + return ProgramImageGenerator::MIF; + } else if (format == "coe") { + return ProgramImageGenerator::COE; + } else if (format == "vhdl") { + return ProgramImageGenerator::VHDL; + } else if (format == "binary") { + return ProgramImageGenerator::BINARY; + } else { + throw IllegalCommandLine(__FILE__, __LINE__, __func__, + "Unknown image output format: " + format); + } + } +} + +/** + * Returns program image format. + */ +ProgramImageGenerator::OutputFormat +TestGenCmdLineOptions::dataImageFormat() const { + try { + return programImageFormat(); //note: placeholder + } catch (IllegalCommandLine& e) { + throw IllegalCommandLine(__FILE__, __LINE__, __func__, + e.errorMessage()); + } +} diff --git a/openasip/src/bintools/TestGenerator/TestGenCmdLineOptions.hh b/openasip/src/bintools/TestGenerator/TestGenCmdLineOptions.hh new file mode 100644 index 0000000000..dd5e07fde3 --- /dev/null +++ b/openasip/src/bintools/TestGenerator/TestGenCmdLineOptions.hh @@ -0,0 +1,95 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file TestGenCmdLineOptions.hh + * + * Declaration of TestGenCmdLineOptions class. + * + * Created on: 24.2.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef TESTGENCMDLINEOPTIONS_HH +#define TESTGENCMDLINEOPTIONS_HH + +#include +#include +#include + +#include "CmdLineOptions.hh" +#include "Exception.hh" +#include "ProgramImageGenerator.hh" + +class TCEString; +class Path; + +class TestGenCmdLineOptions: public CmdLineOptions { +public: + TestGenCmdLineOptions(); + virtual ~TestGenCmdLineOptions(); + + bool adfDefined() const; + Path adfFilePath() const; + bool idfDefined() const; + Path idfFilePath() const; + bool outputDirDefined() const; + Path outputDir() const; + bool progeOutputDirDefined() const; + Path progeOutputDir() const; + std::set programFiles() const; + bool listGeneratorsDefined() const; + size_t toEnabledListSize() const; + std::string toEnabled(size_t index) const; + size_t toDisabledListSize() const; + std::string toDisabled(size_t index) const; + bool disableAllTestGenerators() const; + bool seedDefined() const; + std::string getSeed() const; + ProgramImageGenerator::OutputFormat programImageFormat() const; + ProgramImageGenerator::OutputFormat dataImageFormat() const; + int dataMemoryWidthInMAUs() const; + + virtual void validate() const; + + virtual void printVersion() const; + virtual void printHelp() const; + +private: + static const TCEString PROGEOUTDIR_OPT; + static const TCEString OUTPUTDIR_OPT; + static const TCEString PROGRAMS_OPT; + static const TCEString LIST_GENERATORS; + static const TCEString ENABLE_LIST_OPT; + static const TCEString DISABLE_LIST_OPT; + static const TCEString DISABLE_ALL_OPT; + static const TCEString SEED_OPT; + static const TCEString ADF_OPT; + static const TCEString IDF_OPT; + static const TCEString IMAGE_FORMAT_OPT; + static const TCEString DMEM_WIDTH_IN_MAUS_OPT; + +}; + +#endif /* TESTGENCMDLINEOPTIONS_HH */ diff --git a/openasip/src/procgen/ProGe/ProGeCmdLineOptions.cc b/openasip/src/procgen/ProGe/ProGeCmdLineOptions.cc index 61682c1dd5..7bc0b0d478 100644 --- a/openasip/src/procgen/ProGe/ProGeCmdLineOptions.cc +++ b/openasip/src/procgen/ProGe/ProGeCmdLineOptions.cc @@ -70,6 +70,7 @@ const string DONT_RESET_ALL = "dont-reset-all"; const string FU_BACKREGISTER_LIST = "fu-back-register"; const string FU_FRONTREGISTER_LIST = "fu-front-register"; const string FU_MIDDLEREGISTER_LIST = "fu-middle-register"; +const string DONT_CARE_INIT = "dont-care-init"; /** * The constructor. @@ -237,6 +238,13 @@ ProGeCmdLineOptions::ProGeCmdLineOptions() : FU_MIDDLEREGISTER_LIST, "Comma separated list of FUs to middle-register."); addOption(fuMiddleRegList); + + BoolCmdLineOptionParser* dontCareInit = new BoolCmdLineOptionParser( + DONT_CARE_INIT, + "Initialize FUGen generated signals as don't care. " + "E.g. some FPGA tool optimizations prefer these."); + addOption(dontCareInit); + } @@ -535,6 +543,15 @@ ProGeCmdLineOptions::resetAllRegisters() const { return !findOption(DONT_RESET_ALL)->isFlagOn(); } +/** + * Returns true if HDL Generated signals should initialize as don't care. + */ +bool +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 0c12a6369e..ee4605632f 100644 --- a/openasip/src/procgen/ProGe/ProGeCmdLineOptions.hh +++ b/openasip/src/procgen/ProGe/ProGeCmdLineOptions.hh @@ -81,6 +81,7 @@ public: std::vector fuBackRegistered() const; std::vector fuFrontRegistered() const; std::vector fuMiddleRegistered() const; + bool dontCareInitialization() const; virtual void printVersion() const; virtual void printHelp() const; diff --git a/openasip/src/tools/LLVMIRTools.cc b/openasip/src/tools/LLVMIRTools.cc new file mode 100644 index 0000000000..b8c2a11657 --- /dev/null +++ b/openasip/src/tools/LLVMIRTools.cc @@ -0,0 +1,620 @@ + +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file LLVMIRTools.cc + * + * Implementation of LLVMIRTools class. + * + * Created on: 17.3.2015 + * @author: Henry Linjam�ki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "LLVMIRTools.hh" + +#include +#include + +#include "Operand.hh" +#include "Operation.hh" +#include "Conversion.hh" +#include "MathTools.hh" + +LLVMIRTools::LLVMIRTools() + : lastReturnedTempIdentifier_(0), lastReturnedGlobalIdentifier_(0) { +} + +LLVMIRTools::~LLVMIRTools() { +} + +LLVMIRTools::Type::Type() + : type_(""), alignment_(0), subTypes_() { +} + +LLVMIRTools::Type::Type(stringCRef type, int alignment) + : type_(type), alignment_(alignment), subTypes_() { +} + +LLVMIRTools::Type::~Type() { +} + +std::string +LLVMIRTools::Type::toString() const { + if (isStructure()) { + std::string structureType("{ "); + for (size_t i = 0; i < subTypes_.size(); i++) { + structureType += TCEString::applyIf(i > 0, ", ") + + subTypes_.at(i).toString(); + } + structureType += " }"; + return structureType; + } else if (type_.empty()) { + return "void"; + } else { + return type_; + } +} + +void +LLVMIRTools::Type::addSubType(const LLVMIRTools::Type& other) { + assert(this != &other); + subTypes_.push_back(other); +} + +int +LLVMIRTools::Type::getAlignment() const { + return alignment_; +} + +bool +LLVMIRTools::Type::isStructure() const { + return !subTypes_.empty(); // Even if there is only one subtype defined. +} + +bool +LLVMIRTools::Type::isHalfFloat() const { + return type_ == "half"; +} + +const LLVMIRTools::Type::TypeList& +LLVMIRTools::Type::subTypes() const { + return subTypes_; +} + +/** + * Writes target information, which includes datalayout and triple information. + */ +std::string +LLVMIRTools::targetInfo(bool littleEndian) const { + std::string targetInfoStr = "target datalayout = \""; + targetInfoStr += TCEString::applyIf(littleEndian,"e", "E"); + if(littleEndian){ + targetInfoStr += "-p:32:32:32-i1:8:8-i8:8:32-i16:16:32-i32:32:32"; + targetInfoStr += "-i64:32:32-f32:32:32-f64:32:32-v64:64:64-v128:128:128"; + targetInfoStr += "-v256:256:256-v512:512:512-v1024:1024:1024"; + targetInfoStr += "-v2048:2048:2048-v4096:4096:4096-a0:0:32-n32"; + } + else{ + targetInfoStr += "-p:32:32-i8:8:32-i16:16:32-i64:32-f64:32-v64:32-"; + targetInfoStr += "v128:32-v256:32-v512:32-v1024:32-a:0:32-n32"; + } + targetInfoStr += "\"\n"; + targetInfoStr += std::string("target triple = \"") + + TCEString::applyIf(littleEndian,"tcele-tut-llvm", "tce-tut-llvm") + + "\"\n"; + + return targetInfoStr; +} + +/** + * Wraps given LLVM code in main function. + */ +std::string +LLVMIRTools::mainBody(stringCRef mainCode) const { + std::string mainStr; + mainStr += std::string("@__dummy__ = internal global i32 0, align 4") + + "\n\n" + + "define void @_start() #0 {" + "\n" + + "entry:\n" + + " tail call void asm sideeffect \".call_global_ctors\", \"\"() #1" + + "\n" + + + mainCode + "\n" + + + " tail call void asm sideeffect \".call_global_dtors\", \"\"() #1" + + "\n" + + " tail call fastcc void @_exit() #2" + "\n" + + " unreachable" + "\n" + + "}" + "\n" + "\n" + + "; Function Attrs: noinline noreturn nounwind" + "\n" + + "define internal fastcc void @_exit() #0 {" + "\n" + + "entry:" + "\n" + + " br label %while.body" + "\n" + "\n" + + "while.body: ; preds = %while.body, %entry" + "\n" + + " store volatile i32 0, ptr @__dummy__, align 4" + "\n" + + " br label %while.body" + "\n" + + "}" + "\n" + "\n" + + "attributes #0 = { noinline noreturn nounwind " + + "\"less-precise-fpmad\"=\"false\" " + + "\"no-frame-pointer-elim\"=\"true\" " + + "\"no-frame-pointer-elim-non-leaf\" " + + "\"no-infs-fp-math\"=\"false\" " + + "\"no-nans-fp-math\"=\"false\" " + + "\"stack-protector-buffer-size\"=\"8\" " + + "\"unsafe-fp-math\"=\"false\" " + + "\"use-soft-float\"=\"false\" }" + "\n" + + "attributes #1 = { nounwind }" + "\n" + + "attributes #2 = { nobuiltin noreturn }" + "\n"; + return mainStr; +} + +/** + * Composes assignment string " = "; + */ +std::string +LLVMIRTools::assignment( + stringCRef identifierField, + stringCRef rest) const { + + return identifierField + " = " + rest; +} + +/** + * Composes string "tail call asm " + * + * Fields maps for returnTypes to returnTypeListField, oper to operationField, + * and args to asmArgumentsField. + */ +std::string +LLVMIRTools::inlineAssembly( + stringCRef operationField, + stringCRef returnTypeListField, + stringCRef asmArgumentsField, + stringCRef metadata) const { + + return std::string("tail call ") + + returnTypeListField + " asm " + "\"" + + operationField + "\", " + asmArgumentsField + + bothNonEmpty(" ", metadata); +} + +/** + * Composes string of "store , , " + * + */ +std::string +LLVMIRTools::store( + stringCRef toStoredField, + stringCRef destinationField, + stringCRef aligment, + bool setVolatile) const { + + return std::string("store ") + + TCEString::applyIf(setVolatile, "volatile ") + + toStoredField + ", " + destinationField + + TCEString::applyIf(!aligment.empty(), ", ") + aligment; +} + +/** + * Composes string of store instruction for given Type. + */ +std::string +LLVMIRTools::store( + const LLVMIRTools::Type& nonAggregateType, + stringCRef toStoredIdentifier, + stringCRef destinationIdentifier, + bool setVolatile) const { + + return std::string("store ") + + TCEString::applyIf(setVolatile, "volatile ") + + nonAggregateType.toString() + " " + toStoredIdentifier + ", " + + "ptr " + destinationIdentifier + ", " + + "align " + Conversion::toString(nonAggregateType.getAlignment()); +} + +/** + * Composes extractvalue instruction string. + */ +std::string +LLVMIRTools::extractValue( + stringCRef destIdentifier, + stringCRef srcIdentifier, + const Type& structureType, + unsigned int fieldSelect) const { + + return destIdentifier + " = extractvalue " + structureType.toString() + + " " + srcIdentifier + ", " + Conversion::toString(fieldSelect); +} + +/** + * Composes string of arguments for inline assembly from given input values. + * + * Example output: "=r,ir,ir"(i32 3, i32 -5) + */ +std::string +LLVMIRTools::inlineAssemblyArguments( + const Operation& operation, + const Operation::InputOperandVector& inputValues) const { + + return "\"" + constraints(operation) + + "\"(" + arguments(operation, inputValues) + ")"; +} + +/** + * Composes Type list from Operation to describe return value types for + * asmCallBody(). + * + */ +LLVMIRTools::Type +LLVMIRTools::outputTypeListOf( + const Operation& operation) const { + + if (operation.numberOfOutputs() == 0) { + return Type(); + } + + if (operation.numberOfOutputs() > 1) { + Type typelist; + for (int i = 0; i < operation.numberOfOutputs(); i++) { + Type type = singleTypeOf(operation.output(i)); + if (type.isHalfFloat()) { + // Half types as return type in asm call does not work well. + // Convert it to integer. + typelist.addSubType(Type("i16", 2)); + } else { + typelist.addSubType(type); + } + } + return typelist; + } else { + Type type = singleTypeOf(operation.output(0)); + if (type.isHalfFloat()) { + return Type("i16", 2); + } else { + return type; + } + } +} + +/** + * Composes string of constraint part of asmCallArgs(). + * + * Example output: "=r,r,r" (without quotes). + */ +std::string +LLVMIRTools::constraints( + const Operation& operation) const { + + std::string constraintList; + + for (int i = 0; i < operation.numberOfOutputs(); i++) { + TCEString::appendToNonEmpty(constraintList, ",") += "=r"; + } + + for (int i = 0; i < operation.numberOfInputs(); i++) { + TCEString::appendToNonEmpty(constraintList, ",") += "r"; + } + return constraintList; +} + +/** + * Writes call argument using inputValues as constants without parentheses. + * + * Example output without quotes: "i32 4, float 2.0". + */ +std::string +LLVMIRTools::arguments( + const Operation& operation, + const Operation::InputOperandVector& inputValues) const { + + std::string argumentList; + for (int i = 0; i < operation.numberOfInputs(); i++) { + TCEString::appendToNonEmpty(argumentList, ", ") += + typeOf(operation.input(i)).toString() + " " + + constant(operation.input(i), inputValues.at(i)); + } + return argumentList; +} + +/** + * Writes global variable declaration and binds it to new global variable. + * + * Use lastGlobalIdentifier() to retrieve the new global variable identifier. + */ +std::string +LLVMIRTools::newGlobalDecl( + stringCRef typeAndInitialValue, + stringCRef aligmentValue) { + + std::string globalDecl; + + globalDecl += TCEString::appendInteger( + "@g", lastReturnedGlobalIdentifier_++); + globalDecl += std::string(" = ") + + "internal global " + + typeAndInitialValue + + ", align " + aligmentValue; + + return globalDecl; +} + +/** + * Return last generates global identifier created by newGlobalDecl(). + * + * The Call is valid if newGlobalDecl() has been called. + */ +std::string +LLVMIRTools::lastGlobalIdentifier() const { + return TCEString::appendInteger("@g", (lastReturnedGlobalIdentifier_ - 1)); +} + +/** + * Return free identifier as "%". + */ +std::string +LLVMIRTools::newIdentifier() { + return std::string("%") + Conversion::toString( + lastReturnedTempIdentifier_++); +} + +/** + * Return alignment string suitable for Operand used in store and load + * instructions. + */ +std::string +LLVMIRTools::alignment( + const Operand& operandInfo) const { + return std::string("align ") + + Conversion::toString(alignmentValue(operandInfo)); +} + +/** + * Return alignment value suitable for Operand used in store and load + * instructions. + */ +int +LLVMIRTools::alignmentValue( + const Operand& operandInfo) const { + + int bitWidth = operandInfo.elementCount()*operandInfo.elementWidth(); + return MathTools::ceil_div(bitWidth, 8); +} + +/** + * Converts signed integer value to LLVM IR constant presentation. + */ +std::string +LLVMIRTools::constant( + SIntWord value) const { + return Conversion::toString(value); +} + +/** + * Converts unsigned integer value to LLVM IR constant presentation. + */ +std::string +LLVMIRTools::constant( + UIntWord value) const { + return Conversion::toString(value); +} + +/** + * Returns half float in 0xH#### format. + */ +std::string +LLVMIRTools::constant( + HalfFloatWord value) const { + return std::string("0xH") + + Conversion::toHexString(value.getBinaryRep(), 4, false); +} + +/** + * Returns float in hex string representation. + */ +std::string +LLVMIRTools::constant( + FloatWord value) const { + return Conversion::doubleToHexString(value); +} + +std::string +LLVMIRTools::constant( + DoubleWord value) const { + return Conversion::doubleToHexString(value); +} + +/** + * Returns LLVM IR presentation of type using information from Operand. + * + * e.g single operand types returns "i32" or "float" and vector operand types + * return "<4 x i32>". + */ +LLVMIRTools::Type +LLVMIRTools::typeOf( + const Operand& operandInfo) const { + + if (operandInfo.elementCount() > 1) { + std::string vectorType("<"); + vectorType += Conversion::toString(operandInfo.elementCount()) + + " x " + singleTypeOf(operandInfo).toString() + ">"; + return Type(vectorType, alignmentValue(operandInfo)); + } else { + return singleTypeOf(operandInfo); + } +} + +/** + * Returns LLVM IR presentation of pointer type. + * + * Since LLVM 15 this is an opaque ptr type for all operands + */ +std::string +LLVMIRTools::pointerTypeOf( + const Operand&) const { + + return "ptr"; +} + +/** + * Composes string of pointer to type generated from Operand. + * + * Since LLVM 15 this is an opaque ptr type for all operands + */ +std::string +LLVMIRTools::pointerOfTo( + const Operand&, + stringCRef) const { + + return "ptr"; +} + +/** + * Same as TypeOf() but returns underlying Type. + * + * e.g. For vector operand types returns the base type instead of vector-type. + */ +LLVMIRTools::Type +LLVMIRTools::singleTypeOf( + const Operand& operandInfo) const { + + int widthOfType = 0; + if (operandInfo.elementCount() > 1) { + widthOfType = operandInfo.elementWidth(); + } else { + widthOfType = operandInfo.width(); + } + + widthOfType = MathTools::roundUpToPowerTwo(std::max(widthOfType, 8)); + + switch (operandInfo.type()) { + case Operand::SINT_WORD: + case Operand::UINT_WORD: + case Operand::RAW_DATA: + return Type( + "i" + Conversion::toString(widthOfType), + widthOfType/8); + //return Type("i32", 4); + break; + case Operand::HALF_FLOAT_WORD: + return Type("half", 2); + break; + case Operand::FLOAT_WORD: + return Type("float", 4); + break; + case Operand::DOUBLE_WORD: + return Type("double", 8); + break; + case Operand::BOOL: + // Return type for BOOL as byte. + return Type("i8", 1); + break; + default: + assert(false && "Unknown Operand type."); + } + assert(false); + return Type(); +} + +/** + * Converts SimValue to LLVM IR constant presentation. Deducts type from + * Operand. + */ +std::string +LLVMIRTools::constant( + const Operand& operandInfo, + const SimValue& value) const { + + TCEString constantStr; + union { + SIntWord si; + UIntWord ui; + } conv; + + bool isVector = operandInfo.elementCount() > 1; + constantStr.appendIf(isVector, "<"); + + for (int i = 0; i < operandInfo.elementCount(); i++) { + if (isVector) { + constantStr.appendIf(i > 0, ", "); + constantStr.append(singleTypeOf(operandInfo).toString()); + constantStr.append(" "); + } + switch (operandInfo.type()) { + case Operand::SINT_WORD: + conv.ui = value.element(i, operandInfo.elementWidth()); + conv.si = MathTools::signExtendTo( + conv.si, operandInfo.elementWidth()); + constantStr.append(constant(conv.si)); + break; + case Operand::UINT_WORD: + case Operand::RAW_DATA: + constantStr.append( + constant(value.element(i, operandInfo.elementWidth()))); + break; + case Operand::HALF_FLOAT_WORD: + constantStr.append(constant(value.halfFloatElement(i))); + break; + case Operand::FLOAT_WORD: + constantStr.append(constant(value.floatElement(i))); + break; + case Operand::DOUBLE_WORD: + constantStr.append(constant(value.doubleFloatElement(i))); + break; + case Operand::BOOL: + constantStr.append(constant(value.uIntWordElement(i))); + break; + default: + assert(false && "Unknown Operand type."); + } + } + constantStr.appendIf(isVector, ">"); + return constantStr; +} + +/** + * Returns empty string if one of the arguments is empty. Otherwise return + * concatenated string of the arguments. + */ +std::string +LLVMIRTools::bothNonEmpty( + stringCRef str1, + stringCRef str2) const { + if (str1.empty() || str2.empty()) { + return ""; + } else { + return str1 + str2; + } + +} + +/** + * Resets + */ +void +LLVMIRTools::resetIdentifiers() { + lastReturnedTempIdentifier_ = 0; + lastReturnedGlobalIdentifier_ = 0; +} + diff --git a/openasip/src/tools/LLVMIRTools.hh b/openasip/src/tools/LLVMIRTools.hh new file mode 100644 index 0000000000..eee267a57a --- /dev/null +++ b/openasip/src/tools/LLVMIRTools.hh @@ -0,0 +1,159 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file LLVMIRGen.hh + * + * Declaration of LLVMIRGen class. + * + * Created on: 17.3.2015 + * @author: Henry Linjamäki 2015 (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef LLVMIRGEN_HH +#define LLVMIRGEN_HH + +#include +#include + +#include "Operation.hh" +#include "TCEString.hh" + + +class Operand; + +/* + * Helper class to write LLVM IR assembly code sections. + */ +class LLVMIRTools { +public: + LLVMIRTools(); + virtual ~LLVMIRTools(); + + class Type { + public: + typedef std::vector TypeList; + Type(); + Type(stringCRef type, int alignment); + virtual ~Type(); + std::string toString() const; + void addSubType(const Type& other); + int getAlignment() const; + bool isStructure() const; + bool isHalfFloat() const; + const TypeList& subTypes() const; + + private: + std::string type_; + int alignment_; + TypeList subTypes_; + }; + + std::string targetInfo(bool littleEndian = false) const; + std::string mainBody( + stringCRef mainCode) const; + std::string assignment( + stringCRef identifierField, + stringCRef rest) const; + std::string inlineAssembly( + stringCRef operationField, + stringCRef returnTypeListField, + stringCRef asmArgumentsField, + stringCRef metadata = "") const; + std::string store( + stringCRef toStoredField, + stringCRef destinationField, + stringCRef aligment, + bool setVolatile = false) const; + std::string store( + const Type& nonAggregateType, + stringCRef toStoredIdentifier, + stringCRef destinationIdentifier, + bool setVolatile = false) const; + std::string extractValue( + stringCRef destIdentifier, + stringCRef srcIdentifier, + const Type& structureType, + unsigned int fieldSelect) const; + + std::string inlineAssemblyArguments( + const Operation& operation, + const Operation::InputOperandVector& inputValues) const; + Type outputTypeListOf( + const Operation& operation) const; + std::string constraints( + const Operation& operation) const; + std::string arguments( + const Operation& operation, + const Operation::InputOperandVector& inputValues) const; + + std::string newGlobalDecl( + stringCRef typeAndInitialValue, + stringCRef aligmentValue); + std::string newGlobalDecl( + const Operand& operandInfo, + const SimValue& value); + std::string lastGlobalIdentifier() const; + + std::string newIdentifier(); + std::string alignment( + const Operand& operandInfo) const; + int alignmentValue( + const Operand& operandInfo) const; + Type typeOf( + const Operand& operandInfo) const; + std::string pointerTypeOf( + const Operand& operandInfo) const; + std::string pointerOfTo( + const Operand& operandInfo, + stringCRef identifier) const; + + std::string constant( + const Operand& operandInfo, + const SimValue& value) const; + std::string constant( + SIntWord value) const; + std::string constant( + UIntWord value) const; + std::string constant( + HalfFloatWord value) const; + std::string constant( + FloatWord value) const; + std::string constant( + DoubleWord value) const; + void resetIdentifiers(); +private: + + Type singleTypeOf( + const Operand& operandInfo) const; + std::string bothNonEmpty( + stringCRef str1, + stringCRef str2) const; + + unsigned int lastReturnedTempIdentifier_; + unsigned int lastReturnedGlobalIdentifier_; + +}; + +#endif /* LLVMIRGEN_HH */ diff --git a/openasip/src/tools/Makefile.am b/openasip/src/tools/Makefile.am index 38cc8f15aa..18052b3c3d 100644 --- a/openasip/src/tools/Makefile.am +++ b/openasip/src/tools/Makefile.am @@ -11,7 +11,9 @@ libopenasiptools_la_SOURCES = Exception.cc CmdLineOptions.cc CmdLineOptionParser PluginTools.cc Conversion.cc StringTools.cc DataObject.cc SimValue.cc \ ConfigurationFile.cc ProcessorConfigurationFile.cc Listener.cc \ Informer.cc Options.cc OptionValue.cc CmdLineParser.cc MathTools.cc \ - BitMatrix.cc TCEString.cc HalfFloatWord.cc Reversible.cc IPXact.cc \ + BitMatrix.cc TCEString.cc HalfFloatWord.cc \ + RandomNumberGenerator.cc CompileTools.cc \ + LLVMIRTools.cc Reversible.cc IPXact.cc \ LicenseGenerator.cc if HAVE_SQLITE @@ -46,7 +48,8 @@ include_HEADERS = Application.hh ObjectState.hh Exception.hh Exception.icc BaseT CmdLineOptionParser.hh CmdLineOptionParser.icc MapTools.hh MapTools.icc \ CIStringSet.hh ContainerTools.hh ContainerTools.icc Listener.hh \ Informer.hh Informer.icc HalfFloatWord.hh tce_config_public.h \ - CompilerWarnings.hh Environment.hh LicenseGenerator.hh RISCVTools.hh \ + CompilerWarnings.hh Environment.hh RandomNumberGenerator.hh \ + LicenseGenerator.hh RISCVTools.hh \ RISCVTools.icc REVNO=$(shell which git > /dev/null && echo -n "-r" && git rev-list HEAD | wc -l) @@ -96,6 +99,8 @@ libopenasiptools_la_SOURCES += \ ContainerTools.icc BitMatrix.icc \ TCEString.icc SetTools.icc \ Informer.icc VectorTools.icc \ + RandomNumberGenerator.hh CompileTools.hh\ + LLVMIRTools.hh \ LicenseGenerator.hh RISCVTools.hh \ RISCVTools.icc diff --git a/openasip/src/tools/RandomNumberGenerator.cc b/openasip/src/tools/RandomNumberGenerator.cc new file mode 100644 index 0000000000..44188fb24c --- /dev/null +++ b/openasip/src/tools/RandomNumberGenerator.cc @@ -0,0 +1,176 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file RandomNumberGenerator.cc + * + * Implementation of RandomNumberGenerator class. + * + * Created on: 25.2.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#include "RandomNumberGenerator.hh" +#include "LLVMIRTools.hh" + +#include + +#include "Conversion.hh" + +const RandomNumberGenerator::SeedType +RandomNumberGenerator::DEFAULTSEED = 12345; + +RandomNumberGenerator::RandomNumberGenerator() + : value_(DEFAULTSEED) { +} + + +RandomNumberGenerator::RandomNumberGenerator( + RandomNumberGenerator::SeedType seed) + : value_(seed) { +} + +RandomNumberGenerator::~RandomNumberGenerator() { +} + +RandomNumberGenerator::RNGValueType +RandomNumberGenerator::operator()() { + value_ = 1103515245 * value_ + 12345; + return value_; +} + +void +RandomNumberGenerator::seed(RandomNumberGenerator::SeedType seed) { + value_ = seed; +} + +/** + * Returns new random seed. + */ +RandomNumberGenerator::RNGValueType +RandomNumberGenerator::generateNewSeed() { + return RandomNumberGenerator(time(0))(); +} + +/** + * Returns non-NAN single precision floating point number from given value. + */ +FloatWord +RandomNumberGenerator::asFloat(RNGValueType seed) { + union iIntFloatUnion { + UIntWord i; + float f; + } converter; + converter.i = static_cast(seed); + if ((converter.i & 0x7F800000) == 0x7F800000) { // If NaN, make non-NaN + converter.i &= ~(0x800000); + } + return converter.f; +} + +/** + * Returns non-NAN half precision floating point number from given value. + */ +HalfFloatWord +RandomNumberGenerator::asHalfFloat(RNGValueType seed) { + HalfWord hw = seed; + if ((hw & 0x7C00) == 0x7C00) { // Avoid NaNs and infinities + hw &= ~(0x400); + } + if (hw & 0x7C00) { + // If exp is not zero ensure that msb of significant is one. + hw |= 0x0200; + } + return HalfFloatWord(hw); +} + +/** + * Converts given string to value of SeedType. + * + * First attempts to interpret the string as numerical value. If that fails + * new value is composed from characters of the string. + */ +RandomNumberGenerator::SeedType +RandomNumberGenerator::convertToSeed(const std::string& src) { + try { + return Conversion::toUnsignedInt(src); + } catch (NumberFormatException&) { + RandomNumberGenerator::SeedType seed = DEFAULTSEED; + for (size_t i = 0; i < src.size(); i++) { + seed += static_cast< + RandomNumberGenerator::RNGValueType>(src.at(i)); + } + return seed; + } +} + + +RandomDoubleGenerator::RandomDoubleGenerator() + : RandomNumberGenerator() { +} + +RandomDoubleGenerator::RandomDoubleGenerator(SeedType seed) + : RandomNumberGenerator(seed) { +} + +RandomDoubleGenerator::RNGValueType +RandomDoubleGenerator::operator()() { + SIntWord tmp; + assert(sizeof(double) == 8 && "Unexpected size of double (!= 8)."); + assert(sizeof(SIntWord) == 4); + union { + SIntWord words[2]; + double result; + } conversion; + + // Generate msb bits of double // + tmp = RandomNumberGenerator::operator()(); + + if ((tmp & 0x7FF00000) == 0x7FF00000) { // Avoid NaNs and infinities + tmp &= ~(0x100000); // Change lowest exponent + } + + if (tmp & 0x7FF00000) { + // If exp is not zero ensure that msb of significant is one. + tmp |= 0x80000; + } + +#if HOST_BIGENDIAN == 1 + conversion.words[0] = tmp; +#else + conversion.words[1] = tmp; +#endif + + // Generate lsb bits of double // + tmp = RandomNumberGenerator::operator()(); +#if HOST_BIGENDIAN == 1 + conversion.words[1] = tmp; +#else + conversion.words[0] = tmp; +#endif + + return conversion.result; +} + + diff --git a/openasip/src/tools/RandomNumberGenerator.hh b/openasip/src/tools/RandomNumberGenerator.hh new file mode 100644 index 0000000000..ee41cafd4a --- /dev/null +++ b/openasip/src/tools/RandomNumberGenerator.hh @@ -0,0 +1,84 @@ +/* + Copyright (c) 2002-2015 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. + */ +/* + * @file RandomNumberGenerator.hh + * + * Declaration of RandomNumberGenerator class. + * + * Created on: 25.2.2015 + * @author: Henry Linjamäki (henry.linjamaki-no.spam-tut.fi) + * @note rating: red + */ + +#ifndef RANDOMNUMBERGENERATOR_HH +#define RANDOMNUMBERGENERATOR_HH + +#include + +#include "BaseType.hh" +#include "HalfFloatWord.hh" + +/* + * Pseudo random number generator that aims to be consistent in generating + * same number sequence across platforms. + */ +class RandomNumberGenerator { +public: + + typedef SIntWord SeedType; + typedef SeedType RNGValueType; + + static const SeedType DEFAULTSEED; + + RandomNumberGenerator(); + RandomNumberGenerator(SeedType seed); + virtual ~RandomNumberGenerator(); + + RNGValueType operator()(); + void seed(SeedType seed); + static RNGValueType generateNewSeed(); + static FloatWord asFloat(RNGValueType seed); + static HalfFloatWord asHalfFloat(RNGValueType seed); + static SeedType convertToSeed(const std::string& src); + +private: + + RNGValueType value_; +}; + + +class RandomDoubleGenerator : public ::RandomNumberGenerator { + +public: + + typedef RandomNumberGenerator::SeedType SeedType; + typedef Double RNGValueType; + + RandomDoubleGenerator(); + RandomDoubleGenerator(SeedType seed); + RNGValueType operator()(); +}; + + +#endif /* RANDOMNUMBERGENERATOR_HH */ diff --git a/openasip/tools/scripts/compiletest.sh b/openasip/tools/scripts/compiletest.sh index 2f95954260..8cc29be689 100755 --- a/openasip/tools/scripts/compiletest.sh +++ b/openasip/tools/scripts/compiletest.sh @@ -38,6 +38,7 @@ skipUnitTests=no installAfterCompile=no runSelfTests=no tagIfSuccess=no +numProcesses="" # These are used by the script to figure out how many successive compile test # runs have been executed successfully. After the given count of executions, @@ -60,7 +61,7 @@ mkdir -p ${HOME}/.openasip touch -a "${lastOkRevisionFile}" # Process command line arguments (from Advanced Bash-Scripting Guide). -while getopts "vhngqckmsuiIt" Option +while getopts "vhngqckmsuiItp:" Option do case $Option in v ) @@ -114,6 +115,10 @@ do tagIfSuccess=yes;; + p ) + numProcesses=$OPTARG + ;; + h ) echo "TCE compile&test script (c) 2003-2013 Pekka J��skel�inen"; @@ -164,7 +169,10 @@ do echo " and long) tests pass." echo " Notice that you shouldn't use this if you have local"; echo " uncommitted changes!"; - echo + echo + echo "-p Number of parallel processes to run tests with."; + echo " If no value given, will use one process per CPU core."; + echo exit 0;; @@ -348,6 +356,7 @@ else LOG_DIR="$PWD" fi + # number of lines to tail from error log when mailing the error report LINES_FROM_ERROR_LOG=1500 @@ -408,6 +417,11 @@ firstDialog=yes DENBENCH_TESTDESC=$SYSTEMTEST_LONGLONG_DIR/bintools/Compiler/denbench_verification.testdesc +NUM_PROC_PARAM="" +if [ "x${numProcesses}" != "x" ]; then + NUM_PROC_PARAM="-p $numProcesses" +fi + function link_systemtest_dirs { if [ ! -h $SYSTEMTEST_DIR ] then @@ -643,7 +657,7 @@ function run_system_tests { cd $SYSTEMTEST_DIR { - ../../openasip/tools/scripts/systemtest.py $STPARAM 2>&1 | grep -vE "$SYSTEM_TEST_WARNING_FILTERS" + ../../openasip/tools/scripts/systemtest.py $STPARAM $NUM_PROC_PARAM 2>&1 | grep -vE "$SYSTEM_TEST_WARNING_FILTERS" } 1> $TEMP_FILE 2>&1 log_failure system_testing @@ -663,7 +677,7 @@ function run_long_system_tests { cd $SYSTEMTEST_LONG_DIR { - ../../openasip/tools/scripts/systemtest.py $STPARAM 2>&1 | grep -vE "$SYSTEM_TEST_WARNING_FILTERS" + ../../openasip/tools/scripts/systemtest.py $STPARAM $NUM_PROC_PARAM 2>&1 | grep -vE "$SYSTEM_TEST_WARNING_FILTERS" } 1> $TEMP_FILE 2>&1 log_failure long_testing @@ -683,7 +697,7 @@ function run_longlong_system_tests { cd $SYSTEMTEST_LONGLONG_DIR { - ../../openasip/tools/scripts/systemtest.py $STPARAM 2>&1 | grep -vE "$SYSTEM_TEST_WARNING_FILTERS" + ../../openasip/tools/scripts/systemtest.py $STPARAM $NUM_PROC_PARAM 2>&1 | grep -vE "$SYSTEM_TEST_WARNING_FILTERS" } 1> $TEMP_FILE 2>&1 log_failure longlong_testing diff --git a/testsuite/systemtest_long/bintools/TeGe/data/guard-tests-01.adf b/testsuite/systemtest_long/bintools/TeGe/data/guard-tests-01.adf new file mode 100644 index 0000000000..6cec2599cb --- /dev/null +++ b/testsuite/systemtest_long/bintools/TeGe/data/guard-tests-01.adf @@ -0,0 +1,1108 @@ + + + + + 32 + + + + + + + BOOL + 0 + + + + + + + BOOL + 0 + + + + + + + BOOL + 1 + + + + + + + BOOL + 1 + + + + + + + + zero + 16 + + + + + 32 + + + + + + + BOOL + 0 + + + + + + + BOOL + 0 + + + + + + + BOOL + 1 + + + + + + + BOOL + 1 + + + + + + + + zero + 3 + + + + + 32 + + + + + + + BOOL + 0 + + + + + + + BOOL + 0 + + + + + + + BOOL + 1 + + + + + + + BOOL + 1 + + + + + + + + zero + 3 + + + + + 32 + + + + + + + BOOL + 0 + + + + + + + BOOL + 0 + + + + + + + + zero + 32 + + + + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + + + + B2 + seg1 + + + B0 + seg1 + + + + + + B0 + seg1 + + + B2 + seg1 + + + B1 + seg1 + + + B3 + seg1 + + + + + + B2 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + + + + B0 + seg1 + + + + + + B0 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + + + + B0 + seg1 + + + B3 + seg1 + + + + + + B1 + seg1 + + + + + + B0 + seg1 + + + B1 + seg1 + + + B2 + seg1 + + + B3 + seg1 + + + + + + B0 + seg1 + + + B2 + seg1 + + + + + + B1 + seg1 + + + B0 + seg1 + + + B2 + seg1 + + + + + + B0 + seg1 + + + B2 + seg1 + + + B1 + seg1 + + + + + + B2 + seg1 + + + + + + B1 + seg1 + + + + + + lsu_i1 + 32 + + + + + lsu_o1 + 32 + + + lsu_i2 + 32 + + + ldw + in1t + out1 + + + 0 + 1 + + + 2 + 1 + + + + + ldq + in1t + out1 + + + 0 + 1 + + + 2 + 1 + + + + + ldh + in1t + out1 + + + 0 + 1 + + + 2 + 1 + + + + + stw + in1t + in2 + + + 0 + 1 + + + 0 + 1 + + + + + stq + in1t + in2 + + + 0 + 1 + + + 0 + 1 + + + + + sth + in1t + in2 + + + 0 + 1 + + + 0 + 1 + + + + + ldqu + in1t + out1 + + + 0 + 1 + + + 2 + 1 + + + + + ldhu + in1t + out1 + + + 0 + 1 + + + 2 + 1 + + + + data + + + + + IO_i1 + 8 + + + + + stdout + in1t + + + 0 + 1 + + + + + + + + + MUL_IN1 + 32 + + + + + MUL_IN2 + 32 + + + MUL_OUT + 32 + + + mul + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 1 + 1 + + + + + + + + + ALU_i1 + 32 + + + + + ALU_o1 + 32 + + + ALU_i2 + 32 + + + abs + in1t + out1 + + + 0 + 1 + + + 0 + 1 + + + + + add + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + and + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + eq + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + gt + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + gtu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + ior + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + max + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + maxu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + min + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + minu + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + neg + in1t + out1 + + + 0 + 1 + + + 0 + 1 + + + + + shl + in2 + in1t + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shr + in2 + in1t + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shru + in2 + in1t + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + sub + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + sxhw + in1t + out1 + + + 0 + 1 + + + 0 + 1 + + + + + sxqw + in1t + out1 + + + 0 + 1 + + + 0 + 1 + + + + + xor + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shl1add + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + shl2add + in1t + in2 + out1 + + + 0 + 1 + + + 0 + 1 + + + 0 + 1 + + + + + + + + normal + 16 + 32 + 1 + 1 + + RF_i1 + + + RF_o1 + + + + + normal + 2 + 1 + 1 + 1 + + bool_i1 + + + bool_o1 + + + + + 8 + 0 + 16383 + + + + 8 + 0 + 32767 + + + + + gcu_i1 + 32 + + + + + gcu_i2 + gcu_o1 + 32 + + ra + + jump + pc + + + 0 + 1 + + + + + call + pc + + + 0 + 1 + + + + instructions + 3 + 1 + + + + normal + 1 + 32 + 1 + 1 + 1 + zero + + IMM_rd + +