From nobody Fri Nov 14 15:21:08 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1588231689; cv=none; d=zohomail.com; s=zohoarc; b=EnKIvwVL6Lb5Y+tWLPOnDaWK8XTpXP4onRHGard9PdUYlVgMimAKBKeIKIsF6w4IoBbRgxlDPcecQ2FKb5phIBY1+PsRakuF8N0xnOLcGQqf8eSK0hH5LpwDIn0jY0TLwMtqhCbLKNwmL1P4GalMyIaTa1p6k4+S5Or1SKCsGn0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1588231689; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=+cS+ZIaAla30+g8InOuTW00Y5WtjHtmdHymd1Kua9PI=; b=ZjJurGVT3S5hVaf64QSR1bv8J298dHfDvgtGwDLlwJBSKspm3fcAqJkl0z6V8sApJe3yUFT5YZaRU59/xvj1In8JJdozEG3ZeVMeHcPw+EvpKgvWYgM/i1POyMG4OIR2jbX+4zA1Xv5DDjFKBKnlNwKWkWsuRqLwX+SxlU44DRA= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1588231689439166.22565994510796; Thu, 30 Apr 2020 00:28:09 -0700 (PDT) Received: from localhost ([::1]:40568 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jU3c8-0004zB-5O for importer@patchew.org; Thu, 30 Apr 2020 03:28:08 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34286) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jU3Xb-0004i1-Q1 for qemu-devel@nongnu.org; Thu, 30 Apr 2020 03:23:53 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.90_1) (envelope-from ) id 1jU3W8-0006Yu-49 for qemu-devel@nongnu.org; Thu, 30 Apr 2020 03:23:27 -0400 Received: from smtp2200-217.mail.aliyun.com ([121.197.200.217]:46114) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jU3W6-0006WR-Rn; Thu, 30 Apr 2020 03:21:55 -0400 Received: from L-PF1D6DP4-1208.hz.ali.com(mailfrom:zhiwei_liu@c-sky.com fp:SMTPD_---.HQfovas_1588231302) by smtp.aliyun-inc.com(10.147.40.7); Thu, 30 Apr 2020 15:21:44 +0800 X-Alimail-AntiSpam: AC=PASS; BC=0.07436282|-1; BR=01201311R111ee; CH=green; DM=|CONTINUE|false|; DS=SPAM|spam_ad|0.906831-5.56391e-05-0.0931137; FP=0|0|0|0|0|-1|-1|-1; HT=e02c03306; MF=zhiwei_liu@c-sky.com; NM=1; PH=DS; RN=10; RT=10; SR=0; TI=SMTPD_---.HQfovas_1588231302; From: LIU Zhiwei To: peter.maydell@linaro.org Subject: [RFC PATCH 2/8] riscv: Generate payload scripts Date: Thu, 30 Apr 2020 15:21:33 +0800 Message-Id: <20200430072139.4602-3-zhiwei_liu@c-sky.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20200430072139.4602-1-zhiwei_liu@c-sky.com> References: <20200430072139.4602-1-zhiwei_liu@c-sky.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=121.197.200.217; envelope-from=zhiwei_liu@c-sky.com; helo=smtp2200-217.mail.aliyun.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/04/30 03:21:44 X-ACL-Warn: Detected OS = Linux 3.x [generic] [fuzzy] X-Received-From: 121.197.200.217 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-riscv@nongnu.org, richard.henderson@linaro.org, qemu-devel@nongnu.org, wxy194768@alibaba-inc.com, wenmeng_zhang@c-sky.com, palmer@dabbelt.com, alistair23@gmail.com, alex.bennee@linaro.org, LIU Zhiwei Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Signed-off-by: LIU Zhiwei --- risugen_riscv.pm | 501 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 risugen_riscv.pm diff --git a/risugen_riscv.pm b/risugen_riscv.pm new file mode 100644 index 0000000..092c246 --- /dev/null +++ b/risugen_riscv.pm @@ -0,0 +1,501 @@ +#!/usr/bin/perl -w +##########################################################################= ##### +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# LIU Zhiwei (PingTouGe) - RISC-V implementation +# based on Peter Maydell (Linaro) - initial implementation +##########################################################################= ##### + +# risugen -- generate a test binary file for use with risu +# See 'risugen --help' for usage information. +package risugen_riscv; + +use strict; +use warnings; + +use risugen_common; + +require Exporter; + +our @ISA =3D qw(Exporter); +our @EXPORT =3D qw(write_test_code); + +my $periodic_reg_random =3D 1; + +# +# Maximum alignment restriction permitted for a memory op. +my $MAXALIGN =3D 64; +sub ctz($) +{ + my ($imm) =3D @_; + my $cnt =3D 0; + + if ($imm =3D=3D 0) { + return 0; + } + while (($imm & 1) =3D=3D 0) { + $cnt++; + $imm =3D $imm >> 1; + } + return $cnt; +} + +sub decode_li($) +{ + my ($imm) =3D @_; + my $cnt =3D 0; + my $idx =3D 0; + my $part =3D 0; + my $next =3D 0; + my %result; + + $next =3D $imm; + # only one lui can not hold + while ((($next >> 12) !=3D sextract(($next >> 12) & 0xfffff, 20)) || + (($next & 0xfff) !=3D 0)) { + # at the first time, just eat the least 12 bits + if ($idx =3D=3D 0) { + $part =3D sextract($imm & 0xfff, 12); + $result{"first"} =3D $part; + } else { + $imm =3D $imm - $part; # clear the part before it + $cnt =3D ctz($imm); # add a shift + $imm >>=3D $cnt; + $part =3D sextract($imm & 0xfff, 12); + $result{"mid"}{$idx}{"part"} =3D $part; + $result{"mid"}{$idx}{"cnt"} =3D $cnt; + $next =3D $imm - $part; + } + $idx++; + } + # output a lui + $result{"lui"} =3D sextract(($next >> 12) & 0xfffff, 20); + return %result; +} + +# li is implements as Myraid sequences, just the common way here +sub write_mov_ri($$) +{ + my ($rd, $imm) =3D @_; + + # sequence of li rd, 0x1234567887654321 + # + # 0: 002471b7 lui rd,0x247 + # 4: 8ad1819b addiw rd,rd,-1875 + # 8: 00c19193 slli rd,rd,0xc + # c: f1118193 addi rd,rd,-239 # 0x246f11 + # 10: 00d19193 slli rd,rd,0xd + # 14: d9518193 addi rd,rd,-619 + # 18: 00e19193 slli rd,rd,0xe + # 1c: 32118193 addi rd,rd,801 + my %result =3D decode_li($imm); + + my $len =3D keys %{$result{"mid"}}; + my $i =3D 0; + + # output the last lui + insn32(0x00000037 | $rd << 7 | $result{"lui"} << 12); + # output the sequence of slli and addi + foreach my $key (reverse sort keys %{$result{"mid"}}) { + $i++; + if ($i =3D=3D 1) { + # output the last addiw + insn32(0x0000001b | $rd << 7 | $rd << 15 | + $result{"mid"}{$key}{"part"} << 20); + # slli rd, rd, $result{"mid"}{$key}{"part"} + insn32(0x00001013 | $rd << 7 | $rd << 15 | + $result{"mid"}{$key}{"cnt"} << 20); + } else { + insn32(0x00000013 | $rd << 7 | $rd << 15 | + ($result{"mid"}{$key}{"part"} & 0xfff) << 20); + # slli rd, rd, $result{"mid"}{$key}{"part"} + insn32(0x00001013 | $rd << 7 | $rd << 15 | + $result{"mid"}{$key}{"cnt"} << 20); + } + } + # addi rd, rd, $result{"first"} + insn32(0x00000013 | $rd << 7 | $rd << 15 | ($imm & 0xfff) << 20); +} + +sub write_mov_rr($$) +{ + my ($rd, $rs1) =3D @_; + + # addi $rd, $rs1, 0 + insn32(0x00000013 | $rd << 7 | $rs1 << 15); +} + +sub write_sub_rrr($$$) +{ + my ($rd, $rs1, $rs2) =3D @_; + + # sub $rd, $rs1, $rs2 + insn32(0x40000033 |$rd << 7 | $rs1 << 15 | $rs2 << 20); +} + +my $OP_COMPARE =3D 0; # compare registers +my $OP_TESTEND =3D 1; # end of test, stop +my $OP_SETMEMBLOCK =3D 2; # r0 is address of memory block (8192 bytes) +my $OP_GETMEMBLOCK =3D 3; # add the address of memory block to r0 +my $OP_COMPAREMEM =3D 4; # compare memory block + +# write random fp value of passed precision (1=3Dsingle, 2=3Ddouble) +sub write_random_fpreg_var($) +{ + my ($precision) =3D @_; + my $randomize_low =3D 0; + + if ($precision !=3D 1 && $precision !=3D 2) { + die "write_random_fpreg: invalid precision.\n"; + } + + my ($low, $high); + my $r =3D rand(100); + if ($r < 5) { + # +-0 (5%) + $low =3D $high =3D 0; + $high |=3D 0x80000000 if (rand() < 0.5); + } elsif ($r < 10) { + # NaN (5%) + # (plus a tiny chance of generating +-Inf) + $randomize_low =3D 1; + $high =3D rand(0xffffffff) | 0x7ff00000; + } elsif ($r < 15) { + # Infinity (5%) + $low =3D 0; + $high =3D 0x7ff00000; + $high |=3D 0x80000000 if (rand() < 0.5); + } elsif ($r < 30) { + # Denormalized number (15%) + # (plus tiny chance of +-0) + $randomize_low =3D 1; + $high =3D rand(0xffffffff) & ~0x7ff00000; + } else { + # Normalized number (70%) + # (plus a small chance of the other cases) + $randomize_low =3D 1; + $high =3D rand(0xffffffff); + } + + for (my $i =3D 1; $i < $precision; $i++) { + if ($randomize_low) { + $low =3D rand(0xffffffff); + } + insn32($low); + } + insn32($high); +} + +sub write_random_riscv64_fpdata() +{ + # load floating point registers + my $align =3D 16; + my $datalen =3D 32 * 8 + $align; + write_pc_adr(10, (4 * 4) + ($align - 1)); # insn 1 + write_align_reg(10, $align); # insn 2 + write_jump_fwd($datalen + 4); # insn 3 + + # align safety + for (my $i =3D 0; $i < ($align / 4); $i++) { + insn32(rand(0xffffffff)); + } + + # Todo: As the x10 aligned, it won't be the base address of + # the interesting numbers. + for (my $rt =3D 0; $rt <=3D 31; $rt++) { + write_random_fpreg_var(2); # double + } + + for (my ($rt, $imm) =3D (0, 0); $rt < 32; $rt++) { + # fld rt, x10, imm + insn32(0x00003007 | ($imm << 20) | (10 << 15) | ($rt << 7)); + $imm +=3D 8; + } +} + +sub write_random_register_data($) +{ + my ($fp_enabled) =3D @_; + + if ($fp_enabled) { + # load floating point + write_random_riscv64_fpdata(); + } + + # general purpose registers + for (my $i =3D 1; $i < 32; $i++) { + if ($i !=3D 2 && $i !=3D 3 && $i!=3D 4) { + # TODO full 64 bit pattern instead of 32 + write_mov_ri($i, rand(0xffffffff)); + } + } + write_risuop($OP_COMPARE); +} + +# put PC + offset into a register. +# this must emit an instruction of 8 bytes. +sub write_pc_adr($$) +{ + my ($rd, $imm) =3D @_; + + # auipc rd, 0 + # addi rd, rd, imm + insn32(0x00000017 | $rd << 7); + insn32(0x00000013 | $imm << 20 | $rd << 15 | $rd << 7); +} + +# clear bits in register to satisfy alignment. +# Must use exactly 4 instruction-bytes +sub write_align_reg($$) +{ + my ($rd, $align) =3D @_; + my $imm =3D (-$align) & 0xfff; + die "bad alignment!" if ($align < 2); + + # andi rd, rd, ~(align - 1) + insn32(0x00007013 | $imm << 20 | $rd << 15 | $rd << 7); +} + +# jump ahead of n bytes starting from next instruction +sub write_jump_fwd($) +{ + my ($len) =3D @_; + + # 31 30 21 20 19 12 11 7 6 0 + # imm[20] imm[10:1] imm[11] imm[19:12] rd opcode + # 1 10 1 8 5 7 + # offset[20:1] dest JAL + my ($imm20, $imm1, $imm11, $imm12) =3D (($len & 0x100000) >> 20, ($len= & 0x7fe) >> 1, + ($len & 0x800) >> 11, ($len & 0x= ff000) >> 12); + + # jal x0, len + insn32(0x0000006f | $imm20 << 31 | $imm1 << 21 | $imm11 << 20 | + $imm12 << 12); +} + +sub write_memblock_setup() +{ + # Write code which sets up the memory block for loads and stores. + # We set r0 to point to a block of 16K length + # of random data, aligned to the maximum desired alignment. + + my $align =3D $MAXALIGN; + my $datalen =3D 16384 + $align; + if (($align > 255) || !is_pow_of_2($align) || $align < 4) { + die "bad alignment!"; + } + + # set x10 to (datablock + (align-1)) & ~(align-1) + # datablock is at PC + (4 * 5 instructions) =3D PC + 20 + write_pc_adr(10, (4 * 5) + ($align - 1)); # insn 1 + write_align_reg(10, $align); # insn 2 + write_risuop($OP_SETMEMBLOCK); # insn 3 + write_jump_fwd($datalen + 4); # insn 4 + + for (my $i =3D 0; $i < $datalen / 4; $i++) { + insn32(rand(0xffffffff)); + } + # next: +} + +# Functions used in memory blocks to handle addressing modes. +# These all have the same basic API: they get called with parameters +# corresponding to the interesting fields of the instruction, +# and should generate code to set up the base register to be +# valid. They must return the register number of the base register. +# The last (array) parameter lists the registers which are trashed +# by the instruction (ie which are the targets of the load). +# This is used to avoid problems when the base reg is a load target. + +# Global used to communicate between align(x) and reg() etc. +my $alignment_restriction; + +sub align($) +{ + my ($a) =3D @_; + if (!is_pow_of_2($a) || ($a < 0) || ($a > $MAXALIGN)) { + die "bad align() value $a\n"; + } + $alignment_restriction =3D $a; +} + +sub write_get_offset() +{ + # Emit code to get a random offset within the memory block, of the + # right alignment, into r0 + # We require the offset to not be within 256 bytes of either + # end, to (more than) allow for the worst case data transfer, which is + # 16 * 64 bit regs + my $offset =3D (rand(16384 - 8192) + 4096) & ~($alignment_restriction = - 1); + write_mov_ri(10, $offset); + write_risuop($OP_GETMEMBLOCK); +} + +sub reg($@) +{ + my ($base, @trashed) =3D @_; + write_get_offset(); + # Now x10 is the address we want to do the access to, + # so just move it into the basereg + if ($base !=3D 10) { + write_mov_rr($base, 10); + write_mov_ri(10, 0); + } + if (grep $_ =3D=3D $base, @trashed) { + return -1; + } + return $base; +} + +sub reg_plus_imm($$@) +{ + # Handle reg + immediate addressing mode + my ($base, $imm, @trashed) =3D @_; + if ($imm =3D=3D 0) { + return reg($base, @trashed); + } + + write_get_offset(); + # Now x10 is the address we want to do the access to, + # so set the basereg by doing the inverse of the + # addressing mode calculation, ie base =3D x10 - imm + # We could do this more cleverly with a sub immediate. + if ($base !=3D 10) { + write_mov_ri($base, $imm); + write_sub_rrr($base, 10, $base); + # Clear x10 to avoid register compare mismatches + # when the memory block location differs between machines. + write_mov_ri(10, 0); + } else { + # We borrow x11 as a temporary (not a problem + # as long as we don't leave anything in a register + # which depends on the location of the memory block) + write_mov_ri(11, $imm); + write_sub_rrr($base, 10, 11); + } + if (grep $_ =3D=3D $base, @trashed) { + return -1; + } + return $base; +} + +sub gen_one_insn($) +{ + # Given an instruction-details array, generate an instruction + my $constraintfailures =3D 0; + + INSN: while(1) { + my ($rec) =3D @_; + my $insn =3D int(rand(0xffffffff)); + my $insnname =3D $rec->{name}; + my $insnwidth =3D $rec->{width}; + my $fixedbits =3D $rec->{fixedbits}; + my $fixedbitmask =3D $rec->{fixedbitmask}; + my $constraint =3D $rec->{blocks}{"constraints"}; + my $memblock =3D $rec->{blocks}{"memory"}; + + $insn &=3D ~$fixedbitmask; + $insn |=3D $fixedbits; + + if (defined $constraint) { + # user-specified constraint: evaluate in an environment + # with variables set corresponding to the variable fields. + my $v =3D eval_with_fields($insnname, $insn, $rec, "constraint= s", $constraint); + if (!$v) { + $constraintfailures++; + if ($constraintfailures > 10000) { + print "10000 consecutive constraint failures for $insn= name constraints string:\n$constraint\n"; + exit (1); + } + next INSN; + } + } + + # OK, we got a good one + $constraintfailures =3D 0; + + my $basereg; + + if (defined $memblock) { + # This is a load or store. We simply evaluate the block, + # which is expected to be a call to a function which emits + # the code to set up the base register and returns the + # number of the base register. + # we use 16 for riscv64, although often unnecessary and overki= ll. + align(16); + $basereg =3D eval_with_fields($insnname, $insn, $rec, "memory"= , $memblock); + } + + insn32($insn); + + if (defined $memblock) { + # Clean up following a memory access instruction: + # we need to turn the (possibly written-back) basereg + # into an offset from the base of the memory block, + # to avoid making register values depend on memory layout. + # $basereg -1 means the basereg was a target of a load + # (and so it doesn't contain a memory address after the op) + if ($basereg !=3D -1) { + write_mov_ri($basereg, 0); + } + } + return; + } +} + +sub write_risuop($) +{ + # instr with bits (6:0) =3D=3D 1 1 0 1 0 1 1 are UNALLOCATED + my ($op) =3D @_; + insn32(0x0000006b | $op << 8); +} + +sub write_test_code($) +{ + my ($params) =3D @_; + + my $numinsns =3D $params->{ 'numinsns' }; + my $fp_enabled =3D $params->{ 'fp_enabled' }; + my $outfile =3D $params->{ 'outfile' }; + + my %insn_details =3D %{ $params->{ 'details' } }; + my @keys =3D @{ $params->{ 'keys' } }; + + print "Enter write code", "\n"; + open_bin($outfile); + + # TODO better random number generator? + srand(0); + + print "Generating code using patterns: @keys...\n"; + progress_start(78, $numinsns); + + if (grep { defined($insn_details{$_}->{blocks}->{"memory"}) } @keys) { + write_memblock_setup(); + } + + # memblock setup doesn't clean its registers, so this must come afterw= ards. + write_random_register_data($fp_enabled); + + for my $i (1..$numinsns) { + my $insn_enc =3D $keys[int rand (@keys)]; + #dump_insn_details($insn_enc, $insn_details{$insn_enc}); + gen_one_insn($insn_details{$insn_enc}); + write_risuop($OP_COMPARE); + # Rewrite the registers periodically. This avoids the tendency + # for the VFP registers to decay to NaNs and zeroes. + if ($periodic_reg_random && ($i % 100) =3D=3D 0) { + write_random_register_data($fp_enabled); + } + progress_update($i); + } + write_risuop($OP_TESTEND); + progress_end(); + close_bin(); +} + +1; --=20 2.23.0