/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) Martin Schoeberl <martin@jopdesign.com>
Thomas B. Preusser <thomas.preusser@tu-dresden.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package csp.stream;
import csp.NoC;
import csp.PrivateScope;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.engines.AESLightEngine;
import com.jopdesign.sys.Native;
import java.util.Random;
import joprt.RtThread;
/**
* Streaming benchmark with four processing stages:
*
* 1. Data Generation 2. Encryption 3. Decryption 4. Verification
*
* This benchmark can, by its nature, not scale beyond 4 independent Threads.
* The measured performance will typically scale even less due to
* platform-dependent imbalances of the computational complexities of the
* individual stages. Moreso, the serialized version running within a single
* Thread may even perform best on platforms with significant cache memory due
* to the improved locality. Thus, this benchmark also evaluates the performance
* of the inter-Thread communication.
*
* @author Thomas B. Preusser <thomas.preusser@tu-dresden.de>
* @author Martin Schoeberl <martin@jopdesign.com>
*/
public class AESCsp {
final static int BLOCK_SIZE = 128; // This MUST be a multiple of 16.
final Source source;
final Encrypt enc;
final Decrypt dec;
final Sink sink;
private int blockCnt;
static volatile boolean finished;
public AESCsp() {
// unused stuff should be removed - not queue with CSP
final CipherParameters params;
{ // Encryption Parameters
final byte[] key = new byte[16];
final Random rnd = new Random(127);
for (int i = key.length; i > 0; key[--i] = (byte) rnd.nextInt())
;
params = new KeyParameter(key);
}
source = new Source();
enc = new Encrypt(params);
dec = new Decrypt(params);
sink = new Sink();
}
public String toString() {
return "AESSPM";
}
protected void reset(int cnt) {
finished = false;
blockCnt = cnt;
}
private class Source implements Runnable {
public void run() {
PrivateScope scope = new PrivateScope(1000);
Runnable r = new Runnable() {
public void run() {
Random rnd = new Random();
rnd.setSeed(127);
int cnt = 0;
byte[] block = new byte[BLOCK_SIZE];
while (cnt < blockCnt) {
for (int i = 0; i < block.length; i++) {
block[i] = (byte) rnd.nextInt();
}
++cnt;
// send data
while ((Native.rd(NoC.NOC_REG_STATUS) & NoC.NOC_MASK_SND) != 0) {
// nop
}
Native.wr(2, NoC.NOC_REG_SNDDST);
Native.wr(BLOCK_SIZE, NoC.NOC_REG_SNDCNT);
for (int i = 0; i < BLOCK_SIZE; ++i) {
Native.wr(block[i], NoC.NOC_REG_SNDDATA);
}
}
}
};
scope.enter(r);
}
}
private class Encrypt implements Runnable {
CipherParameters p;
public Encrypt(CipherParameters params) {
p = params;
}
public void run() {
PrivateScope scope = new PrivateScope(1000);
Runnable r = new Runnable() {
public void run() {
int cnt = 0;
byte[] ciph = new byte[BLOCK_SIZE];
byte[] block = new byte[BLOCK_SIZE];
final byte[] key = new byte[16];
final Random rnd = new Random(127);
for (int i = key.length; i > 0; key[--i] = (byte) rnd.nextInt())
;
CipherParameters p = new KeyParameter(key);
final BlockCipher crypt;
crypt = new AESLightEngine();
crypt.init(true, p);
while (cnt < blockCnt) {
// receive one block
while (!((Native.rd(NoC.NOC_REG_STATUS) & NoC.NOC_MASK_RCV) != 0))
;
for (int i = 0; i < BLOCK_SIZE; ++i) {
block[i] = (byte) Native.rd(NoC.NOC_REG_RCVDATA);
}
Native.wr(1, NoC.NOC_REG_RCVRESET); // aka writeReset();
int ofs = BLOCK_SIZE;
do {
ofs -= crypt.getBlockSize();
crypt.processBlock(block, ofs, ciph, ofs);
} while (ofs > 0);
// send data
while ((Native.rd(NoC.NOC_REG_STATUS) & NoC.NOC_MASK_SND) != 0) {
// nop
}
Native.wr(3, NoC.NOC_REG_SNDDST);
Native.wr(BLOCK_SIZE, NoC.NOC_REG_SNDCNT);
for (int i = 0; i < BLOCK_SIZE; ++i) {
Native.wr(ciph[i], NoC.NOC_REG_SNDDATA);
}
++cnt;
}
}
};
scope.enter(r);
}
}
private class Decrypt implements Runnable {
CipherParameters p;
public Decrypt(CipherParameters params) {
p = params;
}
public void run() {
PrivateScope scope = new PrivateScope(1000);
Runnable r = new Runnable() {
public void run() {
byte[] block = new byte[BLOCK_SIZE];
byte[] deciph = new byte[BLOCK_SIZE];
int cnt = 0;
final byte[] key = new byte[16];
final Random rnd = new Random(127);
for (int i = key.length; i > 0; key[--i] = (byte) rnd.nextInt())
;
CipherParameters p = new KeyParameter(key);
final BlockCipher decrypt;
decrypt = new AESLightEngine();
decrypt.init(false, p);
while (cnt < blockCnt) {
// receive one block
while (!((Native.rd(NoC.NOC_REG_STATUS) & NoC.NOC_MASK_RCV) != 0))
;
for (int i = 0; i < BLOCK_SIZE; ++i) {
block[i] = (byte) Native.rd(NoC.NOC_REG_RCVDATA);
}
Native.wr(1, NoC.NOC_REG_RCVRESET); // aka writeReset();
int ofs = BLOCK_SIZE;
do {
ofs -= decrypt.getBlockSize();
decrypt.processBlock(block, ofs, deciph, ofs);
} while (ofs > 0);
// send data
while ((Native.rd(NoC.NOC_REG_STATUS) & NoC.NOC_MASK_SND) != 0) {
// nop
}
Native.wr(4, NoC.NOC_REG_SNDDST);
Native.wr(BLOCK_SIZE, NoC.NOC_REG_SNDCNT);
for (int i = 0; i < BLOCK_SIZE; ++i) {
Native.wr(deciph[i], NoC.NOC_REG_SNDDATA);
}
++cnt;
}
}
};
scope.enter(r);
}
}
private class Sink implements Runnable {
boolean ok = true;
public void run() {
PrivateScope scope = new PrivateScope(1000);
Runnable r = new Runnable() {
public void run() {
byte[] block = new byte[BLOCK_SIZE];
Random rnd = new Random();
rnd.setSeed(127);
int cnt = 0;
while (cnt < blockCnt) {
// receive one block
while (!((Native.rd(NoC.NOC_REG_STATUS) & NoC.NOC_MASK_RCV) != 0))
;
for (int i = 0; i < BLOCK_SIZE; ++i) {
block[i] = (byte) Native.rd(NoC.NOC_REG_RCVDATA);
}
Native.wr(1, NoC.NOC_REG_RCVRESET); // aka writeReset();
for (int i = 0; i < block.length; i++) {
if (block[i] != (byte) rnd.nextInt())
ok = false;
}
++cnt;
}
}
};
scope.enter(r);
finished = true;
}
}
public static void main(String[] args) {
AESCsp aes = new AESCsp();
// Initialization for benchmarking
int start = 0;
int stop = 0;
int time = 0;
System.out.println("AES Benchmark with SPM and NoC");
int nrCpu = Runtime.getRuntime().availableProcessors();
if (nrCpu < 3) {
throw new Error("Not enough CPUs");
}
// ni must be translated from proc index to NoC address!
new RtThread(aes.source, 1, 1000).setProcessor(1);
new RtThread(aes.enc, 1, 1000).setProcessor(2);
new RtThread(aes.dec, 1, 1000).setProcessor(3);
new RtThread(aes.sink, 1, 1000).setProcessor(4);
aes.reset(1000);
// start the other CPUs
System.out.println("starting cpus.");
RtThread.startMission();
start = (int) System.currentTimeMillis();
while (!finished) {
;
}
// End of measurement
stop = (int) System.currentTimeMillis();
System.out.println("StartTime: " + start);
System.out.println("StopTime: " + stop);
time = stop - start;
System.out.println("TimeSpent: " + time);
System.out.println("Result = " + aes.sink.ok);
}
}