/*****************************************************************************
 *
 * Copyright (c) 2018-19, Eduard Gerlits, Paula Herber, Josef Treus
 * Westfälische Wilhelms-Universität Münster, AG Embedded Systems 
 * All rights reserved.
 *
 * This file is part of SysCSec
 *
 * SysCSec 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.
 *
 * SysCSec 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 SysCSec.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *  Please report any problems or bugs to: sysc.sec@uni-muenster.de
 *
 ****************************************************************************/

#ifndef _FW_H_
#define _FW_H_

#include <systemc.h>
#include <climits>

#include <cstdlib>
#include <time.h>
#include "RessourceHandler.cpp"

#include "commons.h"

/*
The Firmware module FW is the software interface for the control of the modular arithmetic processor (MAP), which can execute the following operations on elliptic curves: Modular Addition, Modular Subtraction, Modular Multiplication, Modular Division, point addition, point doubling, scalar point multiplication, ECDSA signature generation and verification, ECDSA key generation.
*/

SC_MODULE(FWModule){

	/// in ports

	// Clock
	sc_in<bool> clk;

	/// SW-Ports

	// control point for operation selection
	sc_fifo_in<sc_uint<4> > ecc_control;

	// data ports for input parameters
	sc_fifo_in<sc_biguint<160> > data_in0;
	sc_fifo_in<sc_biguint<160> > data_in1;
	sc_fifo_in<sc_biguint<160> > data_in2;
	sc_fifo_in<sc_biguint<160> > data_in3;
	sc_fifo_in<sc_biguint<160> > data_in4;
	sc_fifo_in<sc_biguint<160> > data_in5;
	sc_fifo_in<sc_biguint<160> > data_in6;
	sc_fifo_in<sc_biguint<160> > data_in7;
	sc_fifo_in<sc_biguint<160> > data_in8;
	sc_fifo_in<sc_biguint<160> > data_in9;

	/// HW-Ports
	sc_in<sc_biguint<160> > map_out;
	sc_in<bool> map_done;

	// out ports

	/// SW-Ports

	// out port for status
	sc_fifo_out<sc_uint<2> > ecc_status;

	// data ports for outputs
	sc_fifo_out<sc_biguint<160> > data_out0;
	sc_fifo_out<sc_biguint<160> > data_out1;
	sc_fifo_out<sc_biguint<160> > data_out2;
	sc_fifo_out<sc_biguint<160> > data_out3;
	sc_fifo_out<sc_biguint<160> > data_out4;
	sc_fifo_out<sc_biguint<160> > data_out5;

	/// HW-Ports
	//  MAP control
	sc_out<sc_biguint<160> > map_x;
	sc_out<sc_biguint<160> > map_y;
	sc_out<sc_biguint<160> > map_m;
	sc_out<sc_uint<3> > map_opcode;

	
	private:

	/* mode defines for which subject power consumption is tracked
	 * useUAF defines whether standard point additioon and doubling is used or the unified addition formulae 
	 */
	int mode;
	bool useUAF;
	// Internal read and write operations
	void MAP_write(sc_biguint<160> x, sc_biguint<160> y, sc_biguint<160> m, sc_uint<3> opcode);
	sc_biguint<160> MAP_read();
	void MAP_reset();

	// Diverse helper functions
	bool equals(sc_biguint<160> p_x, sc_biguint<160> p_y, sc_biguint<160> q_x, sc_biguint<160> q_y);
	bool isInfinityPoint(sc_biguint<160> p_x, sc_biguint<160> p_y);
	bool isInverseTo(sc_biguint<160> p_x, sc_biguint<160> p_y, sc_biguint<160> q_x, sc_biguint<160> q_y, sc_biguint<160> m);
	void writeInfinityPoint(sc_biguint<160> &out_x, sc_biguint<160> &out_y);
	bool isOnCurve(sc_biguint<160> p_x, sc_biguint<160> p_y, sc_biguint<160> m, sc_biguint<160> ec_param_a, sc_biguint<160> ec_param_b);

	// available firmware operations
	public:	
	void mod_add(sc_biguint<160> x, sc_biguint<160> y, sc_biguint<160> m, sc_biguint<160> &out);
	void mod_sub(sc_biguint<160> x, sc_biguint<160> y, sc_biguint<160> m, sc_biguint<160> &out);
	void mod_mul(sc_biguint<160> x, sc_biguint<160> y, sc_biguint<160> m, sc_biguint<160> &out);
	void mod_div(sc_biguint<160> x, sc_biguint<160> y, sc_biguint<160> m, sc_biguint<160> &out);
	void unified_addition_formulae(sc_biguint<160> p1_x, sc_biguint<160> p1_y, sc_biguint<160> p2_x, sc_biguint<160> p2_y,sc_biguint<160> a, sc_biguint<160> m, sc_biguint<160> &out_x, sc_biguint<160> &out_y);
	void pt_add(sc_biguint<160> p1_x, sc_biguint<160> p1_y, sc_biguint<160> p2_x, sc_biguint<160> p2_y,sc_biguint<160> a, sc_biguint<160> m, sc_biguint<160> &out_x, sc_biguint<160> &out_y);
	void pt_double(sc_biguint<160> p1_x, sc_biguint<160> p1_y,sc_biguint<160> a, sc_biguint<160> m, sc_biguint<160> &out_x, sc_biguint<160> &out_y);
	void pt_multiply(sc_biguint<160> p1_x, sc_biguint<160> p1_y, sc_biguint<160> val, sc_biguint<160> a, sc_biguint<160> m,  sc_biguint<160> &out_x, sc_biguint<160> &out_y);
	
	void ecdsa_sign(sc_biguint<160> ec_param_a,  
										sc_biguint<160> p_x, 
										sc_biguint<160> p_y,
										sc_biguint<160> d, 
										sc_biguint<160> n, 
										sc_biguint<160> m, 
										sc_biguint<160> hash,
										sc_biguint<160> &r, 
										sc_biguint<160> &s);

	void ecdsa_verify(sc_biguint<160> ec_param_a, 
						sc_biguint<160> p_x, 
						sc_biguint<160> p_y,
						sc_biguint<160> q_x, 
						sc_biguint<160> q_y, 
						sc_biguint<160> r,
						sc_biguint<160> s,
						sc_biguint<160> n,
						sc_biguint<160> m,
						sc_biguint<160> hash,
						sc_biguint<160> &verification_status);

	void ecdsa_key_pair_generation(sc_biguint<160> ec_param_a, 
										sc_biguint<160> ec_param_b, 
										sc_biguint<160> p_x, 
										sc_biguint<160> p_y, 
										sc_biguint<160> n, 
										sc_biguint<160> m, 
										sc_biguint<160> &d, 
										sc_biguint<160> &q_x, 
										sc_biguint<160> &q_y);


	void num_check();

	// SystemC-Thread
	void exe();


	SC_HAS_PROCESS(FWModule);
	FWModule(sc_module_name name,bool useUAF, int mode): sc_module(name){
		this->mode = mode;
		this->useUAF = useUAF;
		// Connecting
		SC_THREAD(exe);
		sensitive << clk;
	}
	/*
	SC_CTOR(FWModule){
		// Connecting
		SC_THREAD(exe);
		sensitive << clk;
	}
	*/

};

#endif
