arm: Add support for armv8 CRC32 instructions

This patch introduces the ARM A32/T32/A64 CRC Instructions, which are
mandatory since ARMv8.1. The UNPREDICTABLE behaviours are implemented as
follows:
1) CRC32(C)X (64 bit) instructions are decoded as Undefined in Aarch32
2) The instructions support predication in Aarch32
3) Using R15(PC) as source/dest operand is permitted in Aarch32

Change-Id: Iaf29b05874e1370c7615da79a07f111ded17b6cc
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Reviewed-by: Giacomo Gabrielli <giacomo.gabrielli@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/5521
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
diff --git a/src/arch/arm/isa/decoder/arm.isa b/src/arch/arm/isa/decoder/arm.isa
index f0c0dec..9b8b37a 100644
--- a/src/arch/arm/isa/decoder/arm.isa
+++ b/src/arch/arm/isa/decoder/arm.isa
@@ -1,6 +1,6 @@
 // -*- mode:c++ -*-
 
-// Copyright (c) 2010-2013 ARM Limited
+// Copyright (c) 2010-2013,2017 ARM Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -72,6 +72,7 @@
                     0x3: decode OPCODE {
                         0x9: ArmBlxReg::armBlxReg();
                     }
+                    0x4: Crc32::crc32();
                     0x5: ArmSatAddSub::armSatAddSub();
                     0x6: ArmERet::armERet();
                     0x7: decode OPCODE_22 {
diff --git a/src/arch/arm/isa/formats/aarch64.isa b/src/arch/arm/isa/formats/aarch64.isa
index 38e5b15..2c33e24 100644
--- a/src/arch/arm/isa/formats/aarch64.isa
+++ b/src/arch/arm/isa/formats/aarch64.isa
@@ -1202,6 +1202,22 @@
                         return new Asrv64(machInst, rdzr, rn, rm);
                       case 0xb:
                         return new Rorv64(machInst, rdzr, rn, rm);
+                      case 0x10:
+                        return new Crc32b64(machInst, rdzr, rn, rm);
+                      case 0x11:
+                        return new Crc32h64(machInst, rdzr, rn, rm);
+                      case 0x12:
+                        return new Crc32w64(machInst, rdzr, rn, rm);
+                      case 0x13:
+                        return new Crc32x64(machInst, rdzr, rn, rm);
+                      case 0x14:
+                        return new Crc32cb64(machInst, rdzr, rn, rm);
+                      case 0x15:
+                        return new Crc32ch64(machInst, rdzr, rn, rm);
+                      case 0x16:
+                        return new Crc32cw64(machInst, rdzr, rn, rm);
+                      case 0x17:
+                        return new Crc32cx64(machInst, rdzr, rn, rm);
                       default:
                         return new Unknown64(machInst);
                     }
diff --git a/src/arch/arm/isa/formats/data.isa b/src/arch/arm/isa/formats/data.isa
index 3ee178f..909a525 100644
--- a/src/arch/arm/isa/formats/data.isa
+++ b/src/arch/arm/isa/formats/data.isa
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 ARM Limited
+// Copyright (c) 2010,2017 ARM Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -788,7 +788,7 @@
                     }
                 }
             } else if (bits(op1, 3, 2) == 0x2 && bits(op2, 3, 2) == 0x2) {
-                const uint32_t op1 = bits(machInst, 21, 20);
+                const uint32_t op1 = bits(machInst, 22, 20);
                 const uint32_t op2 = bits(machInst, 5, 4);
                 const IntRegIndex rd =
                     (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
@@ -832,6 +832,27 @@
                     if (op2 == 0) {
                         return new Clz(machInst, rd, rm);
                     }
+                    break;
+                  case 0x4:
+                    switch (op2) {
+                      case 0x0:
+                        return new Crc32b(machInst, rd, rn, rm);
+                      case 0x1:
+                        return new Crc32h(machInst, rd, rn, rm);
+                      case 0x2:
+                        return new Crc32w(machInst, rd, rn, rm);
+                    }
+                    break;
+                  case 0x5:
+                    switch (op2) {
+                      case 0x0:
+                        return new Crc32cb(machInst, rd, rn, rm);
+                      case 0x1:
+                        return new Crc32ch(machInst, rd, rn, rm);
+                      case 0x2:
+                        return new Crc32cw(machInst, rd, rn, rm);
+                    }
+                    break;
                 }
             }
             return new Unknown(machInst);
diff --git a/src/arch/arm/isa/formats/misc.isa b/src/arch/arm/isa/formats/misc.isa
index 43a7cc9..26681e4 100644
--- a/src/arch/arm/isa/formats/misc.isa
+++ b/src/arch/arm/isa/formats/misc.isa
@@ -38,6 +38,37 @@
 // Authors: Gabe Black
 //          Giacomo Gabrielli
 
+def format Crc32() {{
+    decode_block = '''
+    {
+        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
+        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+
+        uint8_t c_poly = bits(machInst, 9);
+        uint8_t sz = bits(machInst, 22, 21);
+        uint8_t crc_select = (c_poly << 2) | sz;
+
+        switch(crc_select) {
+          case 0x0:
+            return new Crc32b(machInst, rd, rn, rm);
+          case 0x1:
+            return new Crc32h(machInst, rd, rn, rm);
+          case 0x2:
+            return new Crc32w(machInst, rd, rn, rm);
+          case 0x4:
+            return new Crc32cb(machInst, rd, rn, rm);
+          case 0x5:
+            return new Crc32ch(machInst, rd, rn, rm);
+          case 0x6:
+            return new Crc32cw(machInst, rd, rn, rm);
+          default:
+            return new Unknown(machInst);
+        }
+    }
+    '''
+}};
+
 def format ArmERet() {{
     decode_block = "return new Eret(machInst);"
 }};
diff --git a/src/arch/arm/isa/includes.isa b/src/arch/arm/isa/includes.isa
index fe0b3ea..caba46d 100644
--- a/src/arch/arm/isa/includes.isa
+++ b/src/arch/arm/isa/includes.isa
@@ -1,6 +1,6 @@
 // -*- mode:c++ -*-
 
-// Copyright (c) 2010, 2012 ARM Limited
+// Copyright (c) 2010, 2012, 2017 ARM Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -95,6 +95,7 @@
 #include "arch/arm/utility.hh"
 #include "arch/generic/memhelpers.hh"
 #include "base/condcodes.hh"
+#include "base/crc.hh"
 #include "cpu/base.hh"
 #include "sim/pseudo_inst.hh"
 #if defined(linux)
diff --git a/src/arch/arm/isa/insts/data64.isa b/src/arch/arm/isa/insts/data64.isa
index 3284d5b..d0ee439 100644
--- a/src/arch/arm/isa/insts/data64.isa
+++ b/src/arch/arm/isa/insts/data64.isa
@@ -226,6 +226,42 @@
         "Dest64 = shiftReg64(Op164, Op264, LSR, intWidth)")
     buildDataXRegInst("rorv", 2,
         "Dest64 = shiftReg64(Op164, Op264, ROR, intWidth)")
+
+    crcCode = '''
+    constexpr uint8_t size_bytes = %(sz)d;
+    constexpr uint32_t poly = %(polynom)s;
+
+    // Initial value is often a previously evaluated
+    // crc value hence is always 32bit in CRC32
+    uint32_t initial_crc = Op164 & 0xFFFFFFFF;
+
+    uint64_t data = htole(Op264);
+    auto data_buffer = reinterpret_cast<uint8_t*>(&data);
+
+    Dest = crc32<poly>(
+        data_buffer,   /* Message register */
+        initial_crc,   /* Initial value of the CRC */
+        size_bytes     /* Size of the original Message */
+    );
+    '''
+    buildDataXRegInst("crc32b", 2,
+        crcCode % {"sz": 1, "polynom": "0x04C11DB7"})
+    buildDataXRegInst("crc32h", 2,
+        crcCode % {"sz": 2, "polynom": "0x04C11DB7"})
+    buildDataXRegInst("crc32w", 2,
+        crcCode % {"sz": 4, "polynom": "0x04C11DB7"})
+    buildDataXRegInst("crc32x", 2,
+        crcCode % {"sz": 8, "polynom": "0x04C11DB7"})
+
+    buildDataXRegInst("crc32cb", 2,
+        crcCode % {"sz": 1, "polynom": "0x1EDC6F41"})
+    buildDataXRegInst("crc32ch", 2,
+        crcCode % {"sz": 2, "polynom": "0x1EDC6F41"})
+    buildDataXRegInst("crc32cw", 2,
+        crcCode % {"sz": 4, "polynom": "0x1EDC6F41"})
+    buildDataXRegInst("crc32cx", 2,
+        crcCode % {"sz": 8, "polynom": "0x1EDC6F41"})
+
     buildDataXRegInst("sdiv", 2, '''
         int64_t op1 = Op164;
         int64_t op2 = Op264;
diff --git a/src/arch/arm/isa/insts/misc.isa b/src/arch/arm/isa/insts/misc.isa
index edeb0f6..4681d50 100644
--- a/src/arch/arm/isa/insts/misc.isa
+++ b/src/arch/arm/isa/insts/misc.isa
@@ -136,7 +136,47 @@
     decoder_output += BasicConstructor.subst(eretIop)
     exec_output += PredOpExecute.subst(eretIop)
 
+    crcCode = '''
+    constexpr uint8_t size_bytes = %(sz)d;
+    constexpr uint32_t poly = %(polynom)s;
 
+    uint32_t data = htole(Op2);
+    auto data_buffer = reinterpret_cast<uint8_t*>(&data);
+
+    Dest = crc32<poly>(
+        data_buffer,   /* Message Register */
+        Op1,           /* Initial Value  of the CRC */
+        size_bytes     /* Size of the original Message */
+    );
+    '''
+
+    def crc32Emit(mnem, implCode, castagnoli, size):
+        global header_output, decoder_output, exec_output
+
+        if castagnoli:
+            # crc32c instructions
+            poly = "0x1EDC6F41"
+        else:
+            # crc32 instructions
+            poly = "0x04C11DB7"
+
+        data = {'sz' : size, 'polynom': poly}
+
+        instCode = implCode % data
+
+        crcIop = InstObjParams(mnem, mnem.capitalize(), "RegRegRegOp",
+                               { "code": instCode,
+                                 "predicate_test": predicateTest }, [])
+        header_output += RegRegRegOpDeclare.subst(crcIop)
+        decoder_output += RegRegRegOpConstructor.subst(crcIop)
+        exec_output += PredOpExecute.subst(crcIop)
+
+    crc32Emit("crc32b", crcCode, False, 1);
+    crc32Emit("crc32h", crcCode, False, 2);
+    crc32Emit("crc32w", crcCode, False, 4);
+    crc32Emit("crc32cb", crcCode, True, 1);
+    crc32Emit("crc32ch", crcCode, True, 2);
+    crc32Emit("crc32cw", crcCode, True, 4);
 
 }};
 
diff --git a/src/base/crc.hh b/src/base/crc.hh
new file mode 100644
index 0000000..132a15c
--- /dev/null
+++ b/src/base/crc.hh
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Giacomo Travaglini
+ */
+
+#ifndef __BASE_CRC_HH__
+#define __BASE_CRC_HH__
+
+#include "base/bitfield.hh"
+
+/**
+ * Evaluate the CRC32 of the first size bytes of a data buffer,
+ * using a specific polynomium and an initial value.
+ * The crc is accomplished by reversing the input, the initial value
+ * and the output (remainder).
+ *
+ * @param data: Input data buffer pointer
+ * @param crc:  Initial value of the checksum
+ * @param size: Number of bytes
+ *
+ * @return 32-bit remainder of the checksum
+ */
+template <uint32_t Poly>
+uint32_t
+crc32(const uint8_t* data, uint32_t crc, std::size_t size)
+{
+    uint32_t byte = 0;
+
+    crc = reverseBits(crc);
+    for (auto i = 0; i < size; i++) {
+        byte = data[i];
+
+        // 32-bit reverse
+        byte = reverseBits(byte);
+        for (auto j = 0; j <= 7; j++) {
+            if ((int)(crc ^ byte) < 0) {
+                crc = (crc << 1) ^ Poly;
+            } else {
+                crc = crc << 1;
+            }
+            byte = byte << 1;
+        }
+    }
+    return reverseBits(crc);
+}
+
+#endif // __BASE_CRC_HH__