aboutsummaryrefslogtreecommitdiff
path: root/risugen
diff options
context:
space:
mode:
authorClaudio Fontana <claudio.fontana@huawei.com>2013-10-02 15:11:57 +0200
committerPeter Maydell <peter.maydell@linaro.org>2014-04-25 13:07:15 +0100
commit8875e9bb81cb62538711c04a9fef712aa34131b4 (patch)
tree386207a2625e91022b574275e70a69b722512b0c /risugen
parent7add45fbb5fd719d2072c5c3964a971c8d54181e (diff)
risugen: implement memory block setup for aarch64
Eventually we will need to split the script into an arch-independent part and an arch-dependent part like the rest of the code. Big thing to note is that this changes the length of the memory block for loads and stores to the actual documented value of ~2K bytes of random data. Before, ~2K 32-bit words of random data were written, for a total of ~8K. Not sure if this was intended, but seems a bug, which gets fixed here. Signed-off-by: Claudio Fontana <claudio.fontana@linaro.org>
Diffstat (limited to 'risugen')
-rwxr-xr-xrisugen99
1 files changed, 82 insertions, 17 deletions
diff --git a/risugen b/risugen
index 64ddb41..bcda2ac 100755
--- a/risugen
+++ b/risugen
@@ -84,7 +84,7 @@ sub insn16($)
}
# for thumb only
-sub align4()
+sub thumb_align4()
{
if ($bytecount & 3) {
insn16(0xbf00); # NOP
@@ -159,7 +159,7 @@ sub write_switch_to_arm()
{
# Switch to ARM mode if we are in thumb mode
if ($is_thumb) {
- align4();
+ thumb_align4();
insn16(0x4778); # bx pc
insn16(0xbf00); # nop
$is_thumb = 0;
@@ -435,23 +435,81 @@ sub is_pow_of_2($)
return ($x > 0) && (($x & ($x - 1)) == 0);
}
+# put PC + offset into a register.
+# this must emit an instruction of 4 bytes.
+sub write_pc_adr($$)
+{
+ my ($rd, $imm) = @_;
+
+ if ($is_aarch64) {
+ # C2.3.5 PC-relative address calculation
+ # The ADR instruction adds a signed, 21-bit value of the pc that fetched this instruction,
+ my ($immhi, $immlo) = ($imm >> 2, $imm & 0x3);
+ insn32(0x10000000 | $immlo << 29 | $immhi << 5 | $rd);
+ } else {
+ # A.2.3 ARM Core Registers:
+ # When executing an ARM instruction, PC reads as the address of the current insn plus 8.
+ $imm -= 8;
+ insn32(0xe28f0000 | $rd << 12 | $imm);
+ }
+}
+
+# clear bits in register to satisfy alignment.
+# Must use exactly 4 instruction-bytes (one instruction on arm)
+sub write_align_reg($$)
+{
+ my ($rd, $align) = @_;
+ die "bad alignment!" if ($align < 2);
+
+ if ($is_aarch64) {
+ # eor rd, rd, (align - 1)
+ # XXX should use log2, popcount, ...
+ my $imm = 0;
+ for (my $cnt = $align; $cnt > 1; $cnt >>= 1) { $imm += 1; }
+ insn32(0xd2400000 | ($imm - 1) << 10 | $rd << 5 | $rd);
+
+ } else {
+ # bic rd, rd, (align - 1)
+ insn32(0xe3c00000 | $rd << 16 | $rd << 12 | ($align - 1));
+ }
+}
+
+# jump ahead of n bytes starting from next instruction
+sub write_jump_fwd($)
+{
+ my ($len) = @_;
+
+ if ($is_aarch64) {
+ # b pc + len
+ insn32(0x14000000 | (($len / 4) + 1));
+ } else {
+ # b pc + len
+ insn32(0xea000000 | (($len / 4) - 1));
+ }
+}
+
sub write_memblock_setup()
{
# Write code which sets up the memory block for loads and stores.
# We just need to set r0 to point to a block of at least 2K length
# of random data, aligned to the maximum desired alignment (32).
+
+ # XXX (claudio) 2K length in bytes? if so, the following is ok,
+ # and is a change from before.
write_switch_to_arm();
my $align = $MAXALIGN;
my $datalen = 2048 + $align;
- die "bad alignment!" if ($align > 255) || !is_pow_of_2($align);
+ die "bad alignment!" if ($align > 255) || !is_pow_of_2($align) || $align < 4;
# set r0 to (datablock + (align-1)) & ~(align-1)
- insn32(0xe28f0000 | ($align + 7)); # add r0, pc, #(8 + $align - 1)
- insn32(0xe3c00000 | ($align - 1)); # bic r0, r0, (align-1)
- write_arm_risuop($OP_SETMEMBLOCK);
- insn32(0xea000000 + ($datalen - 1)); # b next
- for (0..($datalen - 1)) {
+ # datablock is at PC + (4 * 4 instructions) = PC + 16
+ write_pc_adr(0, (4 * 4) + ($align - 1)); # insn 1
+ write_align_reg(0, $align); # insn 2
+ write_risuop($OP_SETMEMBLOCK); # insn 3
+ write_jump_fwd($datalen); # insn 4
+
+ for (my $i = 0; $i < $datalen / 4; $i++) {
insn32(rand(0xffffffff));
}
# next:
@@ -658,13 +716,15 @@ sub gen_one_insn($$)
my $val = ($insn >> $pos) & $mask;
# Check constraints here:
# not allowed to use or modify sp or pc
- next INSN if ($var =~ /^r/ && (($val == 13) || ($val == 15)));
- # Some very arm-specific code to force the condition field
- # to 'always' if requested.
- if ($forcecond) {
- if ($var eq "cond") {
- $insn &= ~ ($mask << $pos);
- $insn |= (0xe << $pos);
+ if (!$is_aarch64) {
+ next INSN if ($var =~ /^r/ && (($val == 13) || ($val == 15)));
+ # Some very arm-specific code to force the condition field
+ # to 'always' if requested.
+ if ($forcecond) {
+ if ($var eq "cond") {
+ $insn &= ~ ($mask << $pos);
+ $insn |= (0xe << $pos);
+ }
}
}
}
@@ -692,8 +752,13 @@ sub gen_one_insn($$)
# 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.
- # Default alignment requirement is 4 bytes.
- align(4);
+ # Default alignment requirement for ARM is 4 bytes,
+ # we use 16 for Aarch64, although often unnecessary and overkill.
+ if ($is_aarch64) {
+ align(16);
+ } else {
+ align(4);
+ }
$basereg = eval_with_fields($insnname, $insn, $rec, "memory", $memblock);
}