1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System; 9 using System.Collections.Generic; 10 using System.Linq; 11 using Antmicro.Renode.Utilities; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.CPU.Disassembler; 14 using Google.FlatBuffers; 15 using FBInstruction; // <auto-generated with flatc -csharp> 16 using Antmicro.Renode.Logging.Profiling; 17 18 namespace Antmicro.Renode.Peripherals.CPU 19 { 20 public class TraceBasedModelFlatBufferWriter : TraceWriter 21 { TraceBasedModelFlatBufferWriter(TranslationCPU cpu, string path, TraceFormat format, bool compress)22 public TraceBasedModelFlatBufferWriter(TranslationCPU cpu, string path, TraceFormat format, bool compress) 23 : base(cpu, path, format, compress) 24 { 25 disassemblyCache = new LRUCache<uint, Disassembler.DisassemblyResult>(CacheSize); 26 instructionsBuffer = new List<InstructionTrace>(); 27 vectorConfig = new Tuple<float, byte, short>(0, 0, -1); 28 } 29 Write(ExecutionTracer.Block block)30 public override void Write(ExecutionTracer.Block block) 31 { 32 var pc = block.FirstInstructionPC; 33 var pcVirtual = block.FirstInstructionVirtualPC; 34 var counter = 0; 35 var hasAdditionalData = block.AdditionalDataInTheBlock.TryDequeue(out var nextAdditionalData); 36 37 while(counter < (int)block.InstructionsCount) 38 { 39 if(!TryReadAndDisassembleInstruction(pc, block.DisassemblyFlags, out var result)) 40 { 41 break; 42 } 43 44 var additionalData = new List<AdditionalData>(); 45 while(hasAdditionalData && (nextAdditionalData.PC == pcVirtual)) 46 { 47 additionalData.Add(nextAdditionalData); 48 hasAdditionalData = block.AdditionalDataInTheBlock.TryDequeue(out nextAdditionalData); 49 } 50 51 var opcode = Misc.HexStringToByteArray(result.OpcodeString.Trim(), true); 52 53 instructionsBuffer.Add(new InstructionTrace(result, opcode, additionalData)); 54 55 pc += (ulong)result.OpcodeSize; 56 pcVirtual += (ulong)result.OpcodeSize; 57 counter++; 58 } 59 FlushBuffer(); 60 } 61 FlushBuffer()62 public override void FlushBuffer() 63 { 64 var builder = new FlatBufferBuilder(InitialFlatBufferSize); 65 var instructions = instructionsBuffer.Select(x => BuildInstructionFlatbuffer(builder, x.Result, x.Opcode, x.AdditionalData)).ToArray(); 66 var instrsVector = Instructions.CreateInstructionsVector(builder, instructions); 67 Instructions.StartInstructions(builder); 68 Instructions.AddInstructions(builder, instrsVector); 69 var instrs = Instructions.EndInstructions(builder); 70 Instructions.FinishInstructionsBuffer(builder, instrs); 71 72 var buf = builder.DataBuffer.ToSizedArray(); 73 // Write the size of the buffer 74 stream.Write(BitConverter.GetBytes(buf.Length), 0, sizeof(int)); 75 stream.Write(buf, 0, buf.Length); 76 stream.Flush(); 77 78 builder.Clear(); 79 instructionsBuffer.Clear(); 80 } 81 Dispose(bool disposing)82 protected override void Dispose(bool disposing) 83 { 84 if(disposed) 85 { 86 return; 87 } 88 89 if(disposing) 90 { 91 FlushBuffer(); 92 stream?.Dispose(); 93 } 94 disposed = true; 95 } 96 BuildInstructionFlatbuffer(FlatBufferBuilder builder, DisassemblyResult result, byte[] opcode, List<AdditionalData> additionalData)97 private Offset<Instruction> BuildInstructionFlatbuffer(FlatBufferBuilder builder, DisassemblyResult result, byte[] opcode, List<AdditionalData> additionalData) 98 { 99 if(result.DisassemblyString == null) 100 { 101 // return a default instruction if the disassembly failed 102 Logger.Log(LogLevel.Warning, $"Disassembly is not available for the instruction at 0x{result.PC:X}. Returning default TBM instruction."); 103 return Instruction.CreateInstruction(builder); 104 } 105 106 var disasString = result.DisassemblyString.Trim(); 107 var sepIdx = disasString.IndexOf('\t'); 108 109 var mnemonic = ""; 110 var operands = new string[0]; 111 112 if(sepIdx == -1) 113 { 114 mnemonic = disasString; 115 } 116 else 117 { 118 mnemonic = disasString.Substring(0, sepIdx); 119 var operandsString = disasString.Substring(sepIdx + 1); 120 operands = operandsString.Split(new string[] { ", " }, StringSplitOptions.None); 121 } 122 123 var mnemonicOffset = builder.CreateString(mnemonic); 124 125 var operandStringOffsetVector = operands.Select(x => builder.CreateString(x)).ToArray(); 126 var operandsVector = Instruction.CreateOperandsVector(builder, operandStringOffsetVector); 127 128 var inputsAndOutputs = TBMRiscVHelper.AsmRegisters(mnemonic, operands); 129 var inputsStringOffsetVector = inputsAndOutputs.Item1.Select(x => builder.CreateString(x)).ToArray(); 130 var outputsStringOffsetVector = inputsAndOutputs.Item2.Select(x => builder.CreateString(x)).ToArray(); 131 var inputsVector = Instruction.CreateInputsVector(builder, inputsStringOffsetVector); 132 var outputsVector = Instruction.CreateOutputsVector(builder, outputsStringOffsetVector); 133 134 var stores = new List<ulong>(); 135 var loads = new List<ulong>(); 136 137 // Execution tracer accesses additional data through hooks mounted at a given PC. 138 // Available types of data include memory access and vector configuration. 139 foreach(var additionalDataEntry in additionalData) 140 { 141 switch(additionalDataEntry.Type) 142 { 143 case AdditionalDataType.MemoryAccess: 144 var type = (additionalDataEntry as MemoryAccessAdditionalData).OperationType; 145 var address = (additionalDataEntry as MemoryAccessAdditionalData).OperationTargetVirtual; 146 switch(type) 147 { 148 case MemoryOperation.MemoryWrite: 149 case MemoryOperation.MemoryIOWrite: 150 stores.Add(address); 151 break; 152 case MemoryOperation.MemoryRead: 153 case MemoryOperation.MemoryIORead: 154 loads.Add(address); 155 break; 156 case MemoryOperation.InsnFetch: // the instruction fetch is not included according to the documentation 157 default: 158 break; 159 } 160 break; 161 case AdditionalDataType.RiscVVectorConfiguration: 162 // Single instruction should have maximum one entry of vector configuration type 163 // so it shouldn't overwrite the previous value. 164 165 var vtype = (additionalDataEntry as RiscVVectorConfigurationData).VectorType; 166 // vector length multiplier 167 var vlmul = BitHelper.GetValue(vtype, 0, 3); 168 var lmul = TBMRiscVHelper.GetVectorLengthMultiplier(vlmul); 169 // element width 170 var vsew = BitHelper.GetValue(vtype, 3, 3); 171 var sew = TBMRiscVHelper.GetSelectedElementWidth(vsew); 172 // vector length 173 var vl = (short)(additionalDataEntry as RiscVVectorConfigurationData).VectorLength; 174 // Keep the current vector configuration to pass it to other vector instructions. 175 vectorConfig = new Tuple<float, byte, short>(lmul, sew, vl); 176 break; 177 case AdditionalDataType.None: 178 default: 179 break; 180 } 181 } 182 183 var isVectorInstruction = TBMRiscVHelper.IsVectorInstruction(inputsAndOutputs.Item1, inputsAndOutputs.Item2); 184 185 var storesVector = Instruction.CreateStoresVector(builder, stores.ToArray()); 186 var loadsVector = Instruction.CreateLoadsVector(builder, loads.ToArray()); 187 188 Instruction.StartInstruction(builder); 189 Instruction.AddAddr(builder, result.PC); 190 Instruction.AddOpcode(builder, BitHelper.ToUInt32(opcode, 0, opcode.Length, false)); 191 Instruction.AddMnemonic(builder, mnemonicOffset); 192 Instruction.AddOperands(builder, operandsVector); 193 Instruction.AddInputs(builder, inputsVector); 194 Instruction.AddOutputs(builder, outputsVector); 195 Instruction.AddIsNop(builder, TBMRiscVHelper.IsNop(mnemonic)); 196 Instruction.AddIsBranch(builder, TBMRiscVHelper.IsBranch(mnemonic)); 197 Instruction.AddBranchTarget(builder, 0); 198 Instruction.AddIsFlush(builder, TBMRiscVHelper.IsFlush(mnemonic)); 199 Instruction.AddIsVctrl(builder, TBMRiscVHelper.IsVctrl(mnemonic)); 200 Instruction.AddLoads(builder, loadsVector); 201 Instruction.AddStores(builder, storesVector); 202 Instruction.AddLmul(builder, isVectorInstruction ? vectorConfig.Item1 : 0); 203 Instruction.AddSew(builder, isVectorInstruction ? vectorConfig.Item2 : (byte)0); 204 Instruction.AddVl(builder, isVectorInstruction ? vectorConfig.Item3 : (short)-1); 205 206 return Instruction.EndInstruction(builder); 207 } 208 209 private bool disposed; 210 private readonly List<InstructionTrace> instructionsBuffer; 211 private Tuple<float, byte, short> vectorConfig; 212 213 private const int InitialFlatBufferSize = 1024; 214 private const int CacheSize = 100000; 215 216 private class InstructionTrace 217 { InstructionTrace(DisassemblyResult result, byte[] opcode, List<AdditionalData> additionalData)218 public InstructionTrace(DisassemblyResult result, byte[] opcode, List<AdditionalData> additionalData) 219 { 220 Result = result; 221 Opcode = opcode; 222 AdditionalData = additionalData; 223 } 224 225 public DisassemblyResult Result { get; } 226 public byte[] Opcode { get; } 227 public List<AdditionalData> AdditionalData { get; } 228 } 229 } 230 } 231