Mentions légales du service

Skip to content
Snippets Groups Projects
Commit d7abfcf8 authored by Pierre-Yves Péneau's avatar Pierre-Yves Péneau
Browse files

cpu: new version of the NopsSimpleCPU.

Main feature: you can combine skip/execute an infinite number of times. See
README for more details.

Change-Id: Ib9f36addd25a23b025ea4f75f739626a5b03a677
parent d7de1c1c
Branches
No related tags found
No related merge requests found
This is the gem5 simulator with an extended CPU to nop instructions # 1. Overview
We added a new CPU model called `NopsSimpleCPU`, which inherits from This is the gem5 [1, 2] simulator with an extended CPU to nop instructions. We added a new CPU model called `NopsSimpleCPU`, which inherits from
`TimingSimpleCPU`. This new CPU differs from two points: `TimingSimpleCPU`. This new CPU :
- it requires a path to a text file as input - overrides the `fetch()` function of the `TimingSimpleCPU`
- it overrides the `fetch()` function of the TimingSimpleCPU - requires a path to a configuration file as input
To specify the configuration file, also called *nop file*, a new option is added: `--nops-file`. This file contains a list of addresses to which specific actions are associated. During the initialization process, the configuration file is read and a hashtable is filled. Then, during the execution phase, each value of PC that has to be fetched is first searched into the hashtable. If found, the CPU execute the associated action to the instruction, *e.g,* skip or execute.
# Usage Here's the list of commands to compile and start gem5 with our CPU model (tested on Fedora only) :
```
# Compile (on systems like Ubuntu/Debian, the scons-2 command is probably scons)
scons-2 CPU_MODELS=AtomicSimpleCPU,TimingSimpleCPU,NopsSimpleCPU -j 1 ./build/ARM/gem5.opt
# Execute
$ ./build/ARM/gem5.opt configs/example/nops.py
-c tests/test-progs/nops/bin/arm/linux/add
--cpu-type NopsSimpleCPU
--nops-file nops.txt
```
To specify the text file, a new option is added: `--nops-file`. This file * `-c` : path to your binary
contains a list of addresses to be replaced by `nop` instruction. Address are * `--cpu-type` : which CPU model do you want to use in gem5. Use `TimingSimpleCPU` for default behavior
in hexadecimal, **without** the "0x" prefix. You have to write **one address * `--nops-file` : described ahead
per line**. During the initialization process, this file is read and a vector
is filled. Then, during the execution phase, each value of PC that has to be
fetched is first searched into this vector. If found, the CPU does not fetch
the instruction and replay the previous instruction.
An example of a command to start gem5 with our CPU model: We added two specific flags to the `NopsSimpleCPU` for debugging purpose:
`./build/ARM/gem5.opt configs/example/nops.py * `NopsSimpleCPUInit` : print the initialisation of data structures before starting the simulation
-c tests/test-progs/nops/bin/arm/linux/add * `NopsSimpleCPUExec` : shows the behavior of the CPU during execution
--cpu-type NopsSimpleCPU
--nops-file nops.txt`
`-c` : path to your binary A generic flag `NopsSimpleCPU` that enables both flags at the same time is also available. To use any of these flags, start gem5 with the `--debug-flags` option, *e.g.* `./build/ARM/gem5.opt --debug-flags=NopsSimpleCPUExec`.
`--cpu-type` : which CPU model do you want to use in gem5. Use TimingSimpleCPU
for default behavior
`--nops-file` : described ahead
You can use two conditions on "noping" an instruction: # 2. Fault model
- the number of times you want this instruction to execute normally
- the number of times you wan to nop this instruction
Both options can be used in a combination, i.e. "wait for 10 normal executions
and then nop for the next 5 executions". These two options has to be specified
on the same line of the specific address. Moreover, it has to be in the order
mentioned ahead, i.e.g. "how many times do we execute normally", then "how many
times do we nop".
## Examples
Nop instruction at 0xCAFE:
`CAFE`
Nop instruction at 0xCAFE after 10 executions:
`CAFE 10`
Nop instruction at 0xCAFE after 10 executions for the 5 following execution:
`CAFE 10 5`
Nop instruction at 0xCAFE only 7 times:
`CAFE 0 7`
In this example, you don't want a normal execution. Hence, the first parameter
has to 0
If no option is specified, the CPU nop the instruction every time. If only one
option is specified, the CPU considers this option as "how many normal execution
should I do before noping", and then nop every time.
# Example
Below an example with simple addition. Here's the code in the binary, expected
result is 45.
We consider a closed fault model to the one experienced by Rivière *et al.* [3]. When an instruction at address `@PC` is replaced by a `nop`, the previous instruction at address `@PC-1` is replayed. Below an example with simple addition. Expected result is `45`.
```
00010570 <asm_add>: 00010570 <asm_add>:
10570: mov r4, #0 10570: mov r4, #0
10574: mov r4, r0 10574: mov r4, r0
...@@ -79,10 +48,10 @@ result is 45. ...@@ -79,10 +48,10 @@ result is 45.
1059c: nop ; (mov r0, r0) 1059c: nop ; (mov r0, r0)
105a0: mov r0, r4 105a0: mov r0, r4
105a4: bx lr 105a4: bx lr
```
If we nop instructions 0x1057c (r4+=2), here's what gem5 executes (start with If we nop instructions `0x1057c` (r4+=2), here's what gem5 executes (start with `--debug-flags=Exec` to display asm execution). Final result is `44`:
--debug-flags=Exec to display asm execution), and the final result is 44: ```
426467000: @asm_add : mov r4, #0 426467000: @asm_add : mov r4, #0
426516000: @asm_add+4 : mov r4, r0 426516000: @asm_add+4 : mov r4, r0
426565000: @asm_add+8 : add r4, r4, #1 426565000: @asm_add+8 : add r4, r4, #1
...@@ -97,20 +66,74 @@ If we nop instructions 0x1057c (r4+=2), here's what gem5 executes (start with ...@@ -97,20 +66,74 @@ If we nop instructions 0x1057c (r4+=2), here's what gem5 executes (start with
426971000: @asm_add+44 : mov r0, r0 426971000: @asm_add+44 : mov r0, r0
427020000: @asm_add+48 : mov r0, r4 427020000: @asm_add+48 : mov r0, r4
427069000: @asm_add+52 : bx lr 427069000: @asm_add+52 : bx lr
```
# 3. Configuration file
This file is given as parameter to the `NopsSimpleCPU` through the `--nops-file` option. It contains a list of addresses with their specific actions. You have to specify **one physical address per line** and it has to be the **first value** on the line. Addresses are in hexadecimal, **without** the *0x* prefix. There is three possible actions for an instruction:
- `s` : the number of times you want to **s**kip this instruction
- `e` : the number of times you want to **e**xecute this instruction normally
- a combination of skip/execute/skip/execute ...
Actions are specified in a chronological order. If no action is specified, the instruction at this address is **always** skipped. If the last action of an instruction is **skip** and the number of required skip is done, the instruction will be always executed if the address is seen again. Conversly, if the last action is **execute** and the number of required execution is done, the instruction will be always skipped if the address is seen again. Below, some examples:
```
81c0 -> always skipped
81c4 s:10 -> skipped 10 times then always executed
81c8 s:10 e:5 -> skipped 10 times, executed 5 times then always executed
81cc e:8 -> executed 8 times then always skipped
8200 s:10 e:5 s:3 e:4 -> skipped 10 times, executed 5 times, skipped 3 times, executed 4 times then always skipped
```
You can use comments in the file with `#` as the first character of the line. Comments cannot be added in the end of a line and have to be on a dedicated line.
```
# This is a comment
8204 s:2 # This a a non-legit comment
```
### 3.1 Synchonisation point
The first non-commented line of the configuration line is particular and defines the **synchronisation point**, *i.e.,* the point where the attack begins. It has to be an address with optionnaly the number of time you want to execute this address before starting the attack. By default this value is set to 1 and you don't have to specify it in the configuration. A value of 1 means that the first time you see the address defined as the synhronisation point, the attack begins. Typically, the synchronisation point is the entry in the `main()` function. Here's an example:
```
# Start the attack when reaching 0x10520
10520
# Start the attack after reaching 0x10524 4 times
10524 4
```
# 4. Reproduce our results
All the source code used in the paper is provided to reproduce the attacks.
Sources are located in `tests/test-progs/nops/src`.
In each folder you can find the file `nops.txt` given as an input for the `--nops-file` option.
We also release ELF executables in `tests/test-progs/nops/bin` which can be used to execute the attacks, in case
your compiler does not reproduce exactly the same physical layout used in our experiments,
Moreover, you can analyse the
attack by generating the objdump of the executable and read the related `nops.txt`.
To compile all executables execute the following command: `make -C tests/test-progs/nops binaries`. **CAUTION**: this operation erases our executables.
To generate the objdump from all executables, use this command: `make -C tests/test-progs/nops dump`
# 5. Miscellaneous
This version of gem5-nops is based on gem5 v19.0.0.0 (9fc9c67b4242c03f165951775be5cd0812f2a705). Below we list the modifications to the original code, which are minimal:
- declare a new CPU model called `NopsSimpleCPU` in `src/cpu/simple/NopsSimpleCPU.py` **(new file, no modification)**
- add source code for this CPU: `src/cpu/simple/nops.{cc,hh}` **(new files, no modification)**
- set the `fetch()` method to `virtual` in `src/cpu/simple/timing.hh` to override implementation **(1 line modified)**
- declare this new CPU as a CPU to compile by default in `src/cpu/simple/SConsopts` **(1 line added)**
- declare object file, source file and debug flags for this new CPU in `src/cpu/simple/Sconscript` **(7 lines added)**
- add a new high-level script (`configs/example/nops.py`) to use this CPU (based on `configs/example/se.py`) **(new file, no modification)**
- add a `nops` folder in `tests/test-progs` with our source code and binaries to reproduce results
Total: 8 lines added, 2 lines modified, 4 new files, 1 new folder for testing only
# References
[1] Binkert *et al.* "The gem5 simulator" (2011)
[2] http://www.gem5.org
Modifications to the original code are minimal: [3] Rivière *et al.* "High precision fault injections on the instruction cache of ARMv7-M architectures" (2015)
- declare a new CPU model called `NopsSimpleCPU` in
`src/cpu/simple/NopsSimpleCPU.py` **(new file, no modification)**
- add source code for this CPU: `src/cpu/simple/nops.{cc,hh}` **(new files, no
modification)**
- set the `fetch()` method to `virtual` in `src/cpu/simple/timing.hh` to
override implementation **(1 line modified)**
- declare this new CPU as a CPU to compile by default in
`src/cpu/simple/SConsopts` **(1 line added)**
- declare object file, source file and debug flags for this new CPU in
`src/cpu/simple/Sconscript` **(5 lines added)**
- add a new high-level script (`configs/example/nops.py`) to use this CPU
(based on `configs/example/se.py`) **(new file, no modification)**
Total: 6 lines added, 2 lines modified, 4 new files
...@@ -50,7 +50,9 @@ if 'TimingSimpleCPU' in env['CPU_MODELS']: ...@@ -50,7 +50,9 @@ if 'TimingSimpleCPU' in env['CPU_MODELS']:
if 'NopsSimpleCPU' in env['CPU_MODELS']: if 'NopsSimpleCPU' in env['CPU_MODELS']:
SimObject('NopsSimpleCPU.py') SimObject('NopsSimpleCPU.py')
Source('nops.cc') Source('nops.cc')
DebugFlag('NopsSimpleCPU') DebugFlag('NopsSimpleCPUInit')
DebugFlag('NopsSimpleCPUExec')
CompoundFlag('NopsSimpleCPU', [ 'NopsSimpleCPUInit', 'NopsSimpleCPUExec' ])
if 'AtomicSimpleCPU' in env['CPU_MODELS'] or \ if 'AtomicSimpleCPU' in env['CPU_MODELS'] or \
'TimingSimpleCPU' in env['CPU_MODELS']: 'TimingSimpleCPU' in env['CPU_MODELS']:
......
/* /*
* Copyright 2020 French Institute for Research in Computer Science and * Copyright (c) 2020, French Institute for Research in Computer Science and
* Automation, All rights reserved * Automation
* Author: Pierre-Yves Peneau <first.last@inria.fr>
* *
* Redistribution and use in source and binary forms, with or without * All rights reserved.
* 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 * Redistribution and use in source and binary forms, with or without
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * modification, are permitted provided that the following conditions are met:
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * * Redistributions of source code must retain the above copyright
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * notice, this list of conditions and the following disclaimer.
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * * Redistributions in binary form must reproduce the above copyright
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * notice, this list of conditions and the following disclaimer in the
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * documentation and/or other materials provided with the distribution.
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * * Neither the name of the <organization> nor the
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * names of its contributors may be used to endorse or promote products
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * derived from this software without specific prior written permission.
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* Authors: Pierre-Yves Péneau * 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 <COPYRIGHT HOLDER> 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.
*/ */
#include "cpu/simple/nops.hh" #include "cpu/simple/nops.hh"
...@@ -34,70 +34,98 @@ ...@@ -34,70 +34,98 @@
#include <fstream> #include <fstream>
#include "debug/NopsSimpleCPU.hh" #include "debug/NopsSimpleCPU.hh"
#include "debug/NopsSimpleCPUExec.hh"
#include "debug/NopsSimpleCPUInit.hh"
using namespace std; using namespace std;
using namespace TheISA; using namespace TheISA;
NopsSimpleCPU::NopsSimpleCPU(NopsSimpleCPUParams *p) NopsSimpleCPU::NopsSimpleCPU(NopsSimpleCPUParams *p)
: TimingSimpleCPU(p), : TimingSimpleCPU(p),
entry_reached(false), synchro_reached(false),
entry_addr(66972), synchro_addr(0),
synchro_exec(1),
nopsFile(p->nops_file) nopsFile(p->nops_file)
{ {
std::ifstream file(nopsFile); std::ifstream file(nopsFile);
std::stringstream tmp; Addr addr;
Addr addr = 0;
int words = 0;
int after = 0;
int occurence = 0;
if (file.is_open()) { if (file.is_open()) {
std::string line; std::string line;
while (getline(file, line)) {
// Check if line is commented // Ignore comments
if ( line.at(0) == '#' ) { while (getline(file, line)) {
if (line.at(0) == '#') {
continue; continue;
} else {
break;
} }
}
// Check how many words there is // First line always contains the synchronisation point and optionally
words = count_words(line); // the number of normal execution for this instruction
std::stringstream tmp;
tmp << std::hex << split_str(line, 0);
tmp >> synchro_addr;
tmp.clear();
// Safety precaution if ( count_words(line) > 1 ) {
assert(words > 0); synchro_exec = std::stoi(split_str(line, 1));
}
// First one is always the address DPRINTF(NopsSimpleCPUInit, "Entry %x e:%d\n",
std::string front = split(line, synchro_addr, synchro_exec);
std::ptr_fun <int, int>(std::isspace)).at(0);
tmp << std::hex << front; // Now, we read the entire file line by line
while (std::getline(file, line)) {
// Ignore commented line
if (line.at(0) == '#') {
continue;
}
// Extract the address
tmp << std::hex << split_str(line, 0);
tmp >> addr; tmp >> addr;
tmp.clear(); tmp.clear();
front.clear();
v_addr.push_back(addr);
// No constraints, put default values // For each new line, we need to create a queue Q to store actions
if ( words == 1 ) { std::queue< std::pair<char, long long int>*>* Q =
v_after.push_back(0); new std::queue<std::pair<char, long long int>*>;
v_occurence.push_back(-1);
} if ( count_words(line) == 1 ) {
// "after" constraint DPRINTF(NopsSimpleCPUInit, "%x -> s:infinite\n", addr);
else if (words == 2) {
after = std::stoi(split(line,
std::ptr_fun <int, int>(std::isspace)).at(1));
v_after.push_back(after);
v_occurence.push_back(-1);
} }
// "after" and "how many" constraint
else { std::string word;
after = std::stoi(split(line, // While there is an action to do (skip address at position 0)
std::ptr_fun <int, int>(std::isspace)).at(1)); for ( int i = 1; i < count_words(line); i++ ) {
occurence = std::stoi(split(line,
std::ptr_fun <int, int>(std::isspace)).at(2)); // Extract word, i.e., action:number
v_after.push_back(after); word = split_str(line, i);
v_occurence.push_back(occurence);
// For each action we need to create a pair P
std::pair<char, long long int>* P =
new std::pair<char, long long int>;
// Extract action and number
P->first = word.at(0);
P->second = std::stoi(word.erase(0, 2));
DPRINTF(NopsSimpleCPUInit, "%x -> %c:%d\n",
addr, P->first, P->second);
// Insert this pair P into the queue
Q->push(P);
} }
// Insert the queue in the map with the address as the key
skip_list.insert({addr, Q});
} }
// Close file
file.close(); file.close();
} }
} }
...@@ -135,110 +163,116 @@ NopsSimpleCPU::fetch() ...@@ -135,110 +163,116 @@ NopsSimpleCPU::fetch()
!curMacroStaticInst; !curMacroStaticInst;
if (needToFetch) { if (needToFetch) {
// Which address has to be fetched ?
Addr fetchPC = (thread->instAddr() & PCMask) + t_info.fetchOffset; Addr fetchPC = (thread->instAddr() & PCMask) + t_info.fetchOffset;
if (fetchPC == entry_addr) { // Before anything, we need to reach the synchronisation point. Until
entry_reached = true; // it is not reached, do not skip instructions
DPRINTF(NopsSimpleCPU, "Entry point reached, start nop\n"); if (fetchPC == synchro_addr && !synchro_reached) {
synchro_exec--;
DPRINTF(NopsSimpleCPUExec, "Entry point reached\n");
if ( synchro_exec <= 0 ) {
synchro_reached = true;
DPRINTF(NopsSimpleCPUExec, "Start skipping\n");
} else {
DPRINTF(NopsSimpleCPUExec, "%d execution(s) remaining\n",
synchro_exec);
}
} }
if (!entry_reached) { if (!synchro_reached) {
TimingSimpleCPU::fetch(); TimingSimpleCPU::fetch();
return; return;
} }
// From here, we've executed the right number of time the address
// specified in synchro_addr
bool nop = false; bool nop = false;
std::pair<bool, int> p = find_in_vector(v_addr, fetchPC); // Find if the address to fetch is an address to skip
std::map<Addr,
// We detect an address to nop std::queue<std::pair<char, long long int>*>*>::iterator it;
if ( p.first ) { it = skip_list.find(fetchPC);
// If true, we have to wait N normal execution(s) before starting
// nop // If true, there is something to do with this address
if (v_after.at(p.second) > 0) { if ( it != skip_list.end() ) {
v_after.at(p.second)--;
DPRINTF(NopsSimpleCPU, // No specific action remaining, just skip
"Fetch 0x%x -> Should NOP, %d execution remaining\n", if ( it->second->empty() ) {
fetchPC, v_after.at(p.second)); DPRINTF(NopsSimpleCPUExec, "Always skip 0x%x\n", fetchPC);
} nop = true;
// This instruction has been executed the right number of times. } else {
else { // If true, "skip" action may be required
// If true, this instruction has a specific rule regarding the if ( it->second->front()->first == 's' ) {
// number of time we should nop it // If true, skip
if (v_occurence.at(p.second) != -1) { if ( it->second->front()->second > 0 ) {
// If true, we still have to nop this instruction it->second->front()->second--;
if (v_occurence.at(p.second) > 0) {
v_occurence.at(p.second)--;
nop = true; nop = true;
DPRINTF(NopsSimpleCPU, DPRINTF(NopsSimpleCPUExec,
"Fetch 0x%x -> NOP, %d NOP remaining\n", "Conditional skip 0x%x, %d remaining \n",
fetchPC, v_occurence.at(p.second)); fetchPC,
it->second->front()->second);
} else {
nop = false;
}
}
// If true, do not skip
else if ( it->second->front()->first == 'e') {
if ( it->second->front()->second > 0 ) {
it->second->front()->second--;
DPRINTF(NopsSimpleCPUExec,
"Conditional execute 0x%x, %d remaining\n",
fetchPC,
it->second->front()->second);
} else {
nop = true;
}
}
// Check if this action is over. If yes, then remove the action
// from the queue. Note that it does not depend on the action.
// When counter reaches zero, it's done.
//
// If this is the last action, removal depends of the nature
// of the action. If the last action is an execute, it means
// that the next action should be a skip. Then we pop the
// action since skipping is the default behavior if there is no
// specific action to do.
// However, if the last action is a skip, it means that the
// next action sould be an execute. Hence, we do not pop the
// last action and leave the counter at zero. Instruction will
// be always executed.
if ( it->second->front()->second == 0 ) {
if ( it->second->size() > 1 ) {
it->second->pop();
} else if ( it->second->front()->first == 'e' ) {
it->second->pop();
} }
} else {
nop = true;
DPRINTF(NopsSimpleCPU, "Fetch 0x%x -> NOP (no rule)\n",
fetchPC);
} }
} }
} }
// If true, advance CPU state without sending the fetch request.
// Otherwise call the normal fetch().
if (nop) { if (nop) {
_status = IcacheWaitResponse; _status = IcacheWaitResponse;
completeIfetch(NULL); completeIfetch(NULL);
// TODO: does not seem mandatory, final tick is the same // TODO: does not seem mandatory, final tick is the same
// FIXME: method has to be set to "public" in timing.hh if you
// want to use it
//TimingSimpleCPU::updateCycleCounts(); //TimingSimpleCPU::updateCycleCounts();
updateCycleCounters(BaseCPU::CPU_STATE_ON); updateCycleCounters(BaseCPU::CPU_STATE_ON);
} } else {
else {
TimingSimpleCPU::fetch(); TimingSimpleCPU::fetch();
} }
} else {
TimingSimpleCPU::fetch();
}
}
/*
Generic function to find an element in vector and its position. It returns a
pair of bool and int i.e.:
bool : if element is present in vector or not.
int : the index of element in vector if found, -1 otherwose
Source: thispointer.com/c-how-to-find-an-element-in-vector-and-get-its-index
*/
template < typename T>
std::pair<bool, int >
NopsSimpleCPU::find_in_vector(const std::vector<T> & vecOfElements,
const T & element)
{
std::pair<bool, int > result;
// Find given element in vector
auto it = std::find(vecOfElements.begin(), vecOfElements.end(), element);
if (it != vecOfElements.end())
{
result.second = distance(vecOfElements.begin(), it);
result.first = true;
} }
else else {
{ TimingSimpleCPU::fetch();
result.first = false;
result.second = -1;
} }
return result;
} }
// Source : http://www.cplusplus.com/forum/general/30929 // Source : http://www.cplusplus.com/forum/general/30929
#include <algorithm>
#include <cctype>
#include <functional>
#include <list>
#include <string>
unsigned int unsigned int
NopsSimpleCPU::count_words(const std::string& s) NopsSimpleCPU::count_words(const std::string& s)
{ {
...@@ -246,28 +280,34 @@ NopsSimpleCPU::count_words(const std::string& s) ...@@ -246,28 +280,34 @@ NopsSimpleCPU::count_words(const std::string& s)
std::replace_if(x.begin(), x.end(), std::replace_if(x.begin(), x.end(),
std::ptr_fun<int, int> (std::isspace), ' '); std::ptr_fun<int, int> (std::isspace), ' ');
x.erase( 0, x.find_first_not_of( " " ) ); x.erase( 0, x.find_first_not_of( " " ) );
if (x.empty())
if (x.empty()) {
return 0; return 0;
}
return std::count(x.begin(), std::unique(x.begin(), x.end()), ' ') + return std::count(x.begin(), std::unique(x.begin(), x.end()), ' ') +
!std::isspace( *s.rbegin()); !std::isspace(*s.rbegin());
}
std::string
NopsSimpleCPU::split_str(std::string line, unsigned int index) {
return split(line, std::ptr_fun <int, int>(std::isspace)).at(index);
} }
// Source : http://www.cplusplus.com/forum/general/30929 // Source : http://www.cplusplus.com/forum/general/30929
template <typename Predicate> template <typename Predicate>
std::vector <std::string> std::vector <std::string>
NopsSimpleCPU::split( const std::string& s, Predicate p ) NopsSimpleCPU::split(const std::string& s, Predicate p)
{ {
std::vector <std::string> result; std::vector <std::string> result;
std::string::const_iterator rtok; std::string::const_iterator rtok;
std::string::const_iterator ltok = std::find_if(s.begin(), s.end(), std::string::const_iterator ltok = std::find_if(s.begin(), s.end(),
std::not1(p)); std::not1(p));
while (ltok != s.end()) while (ltok != s.end()) {
{ rtok = std::find_if(ltok, s.end(), p);
rtok = std::find_if( ltok, s.end(), p ); result.push_back(std::string(ltok, rtok));
result.push_back( std::string( ltok, rtok ) ); ltok = std::find_if(rtok, s.end(), std::not1(p));
ltok = std::find_if( rtok, s.end(), std::not1( p ) );
} }
return result; return result;
......
/* /*
* Copyright 2020 French Institute for Research in Computer Science and * Copyright (c) 2020, French Institute for Research in Computer Science and
* Automation, All rights reserved * Automation
* Author: Pierre-Yves Peneau <first.last@inria.fr>
* *
* Redistribution and use in source and binary forms, with or without * All rights reserved.
* 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 * Redistribution and use in source and binary forms, with or without
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * modification, are permitted provided that the following conditions are met:
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * * Redistributions of source code must retain the above copyright
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * notice, this list of conditions and the following disclaimer.
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * * Redistributions in binary form must reproduce the above copyright
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * notice, this list of conditions and the following disclaimer in the
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * documentation and/or other materials provided with the distribution.
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * * Neither the name of the <organization> nor the
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * names of its contributors may be used to endorse or promote products
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * derived from this software without specific prior written permission.
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* Authors: Pierre-Yves Péneau * 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 <COPYRIGHT HOLDER> 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.
*/ */
#ifndef __CPU_SIMPLE_NOPS_HH__ #ifndef __CPU_SIMPLE_NOPS_HH__
#define __CPU_SIMPLE_NOPS_HH__ #define __CPU_SIMPLE_NOPS_HH__
...@@ -44,28 +45,41 @@ class NopsSimpleCPU : public TimingSimpleCPU ...@@ -44,28 +45,41 @@ class NopsSimpleCPU : public TimingSimpleCPU
private: private:
bool entry_reached; bool synchro_reached;
Addr entry_addr; Addr synchro_addr; // Which address is the synchronisation point
int synchro_exec; // How many times do we have to execute this instruction
std::string nopsFile;
// The following three vectors are correlated. Each address at index i in
// v_addr<> has its constraints at index i in the two other vector.
// All addresses to nop // This map is main data structure.
std::vector<Addr> v_addr; // Each instruction to skip is identified by its address and a queue
// containing the actions on this specific instuction. Action are always
// performed in a FIFO order.
// Actions are specified using a std::pair<char, long long int>, where the
// char represents the action and the integer the number of time we have to
// perform this action. Once this counter reaches zero, the action is
// removed from the FIFO and the next one (if any) will be executed
// Actions can be:
// s: skip
// e: execute
// followed by a number, e.g.:
// s:10 e:2
// means "skip 10 times then execute 2 times"
//
// When there is no action remaining action, behavior depends of the nature
// of the last action:
// * last action was a skip -> always execute from now
// * last action was an execute -> always skip from now
//
// std::map<> provides a research time in O(1) on average, and O(n) at max
// std::queue<> is the normal structure for a FIFO
// std::pair<> is the easiest way to store two informations
std::map<Addr, std::queue<std::pair<char, long long int>*>*> skip_list;
// How many times an address should execute normally std::string nopsFile;
std::vector<int> v_after;
// How many times an address should be "noped"
std::vector<int> v_occurence;
unsigned int count_words( const std::string& s ); unsigned int count_words(const std::string& s);
std::string split_str(std::string line, unsigned int index);
template <typename Predicate> std::vector <std::string> template <typename Predicate> std::vector <std::string>
split( const std::string& s, Predicate p ); split( const std::string& s, Predicate p );
template < typename T> std::pair<bool, int >
find_in_vector(const std::vector<T> & vecOfElements, const T &element);
}; };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment