Computer Science/컴퓨터 구조

[컴퓨터 구조] RISC-V Simulator

바보1 2022. 11. 22. 17:06

사용방법

  1. 리눅스 시스템에서 해당 c 파일을 컴파일을 한다.
  2. 만약 그냥 컴파일 한다면 a.out으로 나온다.
  3. ./a.out runme.hex [0|1] 을 입력하면 된다.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

//clock cycles
long long cycles = 0;

// registers
long long int regs[32];

// program counter
unsigned long pc = 0;

// memory
#define INST_MEM_SIZE 32*1024
#define DATA_MEM_SIZE 32*1024
unsigned long inst_mem[INST_MEM_SIZE]; //instruction memory
unsigned long long data_mem[DATA_MEM_SIZE]; //data memory

//misc. function
int init(char* filename);

void print_cycles();
void print_reg();
void print_pc();


// define architecture size
#define arch_size 32
#define OPCODE 0
#define RS1 1
#define RS2 2
#define RD 3
#define IMM 4
#define RESULT 5

// instruction
unsigned long inst;

// for execution, opcode, rs1, rs2, rd, imm, result
int control[6];

// covert binary number to decimal number
int convert_bin2dec(unsigned short int* bit, int start, int end){
	int value = 0;
	int times = 1;
	for (int i = start; i < end; i++){
		value += bit[i] * times;
		times *= 2;
	}

	return value;
}

// covert decimal number to binary number
unsigned short int* convert_dec2bin(unsigned long inst){
	int rem = 0;
	// unsigned short int bit[32] = {0,};		// instruction bit and initialize
	unsigned short int* bit = (unsigned short int *)malloc(sizeof(int) * arch_size);

	for (int i = 0; i < arch_size; i++) {
		rem = inst % 2;
		bit[i] = rem;
		inst = inst / 2;
	}

	return bit;
}

void convert_1to0(unsigned short int* bit, int start, int end) {
	for (int i = start; i < end; i++) {
		if (bit[i] == 0)
			bit[i] = 1;
		else
			bit[i] = 0;
	}
}

//fetch an instruction from a instruction memory
void fetch() {
	inst = inst_mem[pc / 4];
}

//decode the instruction and read data from register file
void decode() {
	// unsigned short 
	unsigned short int* bit = convert_dec2bin(inst);
	
	// only check add, addi, jal, jalr, lw, sw, beq
	// add	0110011(51), 000, 0000000	R-Type		need add, rs1, rs2, rd
	// addi	0010011(19), 000, n.a		I-Type		need add, rs1, imm, rd
	// jal	1101111(111), n.a, n.a		UJ-Type		need add, 	   imm, rd
	// jalr	1100111(103), 000, n.a		I-Type		need add, rs1, imm, rd
	// lw	0000011(3), 010, n.a		I-Type		need add, rs1, imm, rd
	// sw	0100011(35), 010, n.a		S-Type		need add, rs1, rs2, imm
	// beq	1100011(99), 000, n.a		SB-Type		need add, rs1, rs2, imm
	

	int opcode = convert_bin2dec(bit, 0, 7);
	int rs1 = convert_bin2dec(bit, 15, 20);
	int rs2 = convert_bin2dec(bit, 20, 25);
	int rd = convert_bin2dec(bit, 7, 12);
	int imm = 0;


	switch (opcode)
	{
	case 51:		// add
		break;

	case 19:		// addi
		if (bit[31] == 0)
			imm = convert_bin2dec(bit, 20, 32);
		else {
			convert_1to0(bit, 20, 32);
			imm = convert_bin2dec(bit, 20, 32);
			imm = ~imm;
		}
		break;

	case 111:		// jal
		if (bit[31] == 0){
			imm = (convert_bin2dec(bit, 21 ,31) + 
				bit[20] * 2048 +
				convert_bin2dec(bit, 12, 20) * 4096 +
				bit[31] * 1048576) * 2;
		}
		else{
			convert_1to0(bit, 21, 31);
			convert_1to0(bit, 12, 20);
			convert_1to0(bit, 31, 32);
			convert_1to0(bit, 20, 21);
			imm = convert_bin2dec(bit, 21 ,31) +
				bit[20] * 2048 +
				convert_bin2dec(bit, 12, 20) * 4096 +
				bit[31] * 1048576;

			imm = ~imm;
			imm *= 2;
		}
		break;

	case 103:		// jalr
		imm = convert_bin2dec(bit, 20, 32);
		break;

	case 3:			// lw
		imm = convert_bin2dec(bit, 20, 32);
		break;

	case 35:		// sw
		imm = convert_bin2dec(bit, 25, 32);
		imm = imm * 32 + rd;
		break;

	case 99:		// beq
	if (bit[31] == 0){
		imm = (convert_bin2dec(bit, 8, 12) + 
			convert_bin2dec(bit, 25, 31) * 32 +
			bit[7] * 2048 +
			bit[31] * 4096) * 2;
	}
	else{
		convert_1to0(bit, 8, 12);
		convert_1to0(bit, 25, 31);
		convert_1to0(bit, 7, 8);
		convert_1to0(bit, 31, 32);
		imm = convert_bin2dec(bit, 8, 12) + 
			convert_bin2dec(bit, 25, 31) * 32 +
			bit[7] * 2048 +
			bit[31] * 4096;
		imm = ~imm;
		imm *= 2;
	}
		break;

	default:
		printf("Error Code!\n");
		break;
	}

	control[OPCODE] = opcode;
	control[RS1] = rs1;
    control[RS2] = rs2;
	control[RD] = rd;
    control[IMM] = imm;
}

//perform the appropriate operation 
void exe() {
	switch (control[OPCODE])
	{
	case 51:		// add
		control[RESULT] = regs[control[RS1]] + regs[control[RS2]];
		break;

	case 19:		// addi
		control[RESULT] = regs[control[RS1]] + control[IMM];
		break;

	case 111:		// jal
		break;

	case 103:		// jalr
		control[RESULT] = regs[control[RS1]] + control[IMM];
		break;

	case 3:			// lw
		control[RESULT] = regs[control[RS1]] + control[IMM];
		break;

	case 35:		// sw
		control[RESULT] = regs[control[RS1]] + control[IMM];
		break;

	case 99:		// beq
		control[RESULT] = regs[control[RS1]] - regs[control[RS2]];
		break;

	default:
		printf("Error Code!\n");
		break;
	}
}

//access the data memory
void mem() {
	switch (control[OPCODE])
	{
	case 51:		// add
		pc += 4;
		break;

	case 19:		// addi
		pc += 4;
		break;

	case 111:		// jal
		pc += control[IMM];
		break;

	case 103:		// jalr
		pc = control[RESULT];	
		break;

	case 3:			// lw
		pc += 4;
		control[RESULT] = data_mem[control[RESULT]];
		break;

	case 35:		// sw
		pc += 4;
		data_mem[control[RESULT]] = regs[control[RS2]];
		break;

	case 99:		//beq
		if (control[RESULT] == 0)
			pc += control[IMM];
		else
			pc += 4;
		break;

	default:
		printf("Error Code!\n");
		break;
	}

	
}

//write result of arithmetic operation or data read from the data memory if required
void wb() {
	switch (control[OPCODE])
	{
	case 51:		// add
		if (control[RD] != 0)
			regs[control[RD]] = control[RESULT];
		break;

	case 19:		// addi
		if (control[RD] != 0)
			regs[control[RD]] = control[RESULT];
		break;

	case 111:		// jal
		if (control[RD] != 0)
			regs[control[RD]] = pc - control[IMM] + 4;
		break;

	case 103:		// jalr
		if (control[RD] != 0)
			regs[control[RD]] = control[RESULT];
		break;

	case 3:			// lw
		regs[control[RD]] = control[RESULT];
		break;

	case 35:		// sw
		break;

	case 99:		// beq
		break;

	default:
		printf("Error Code!\n");
		break;
	}
}

int main(int ac, char* av[])
{
	
	if (ac < 3)		// 인자가 3개 이하로 들어오면 오류 출력
	{
		printf("./riscv_sim filename mode\n");
		return -1;
	}


	char done = 0;
	if (init(av[1]) != 0)		// av[1]에는 filename이 들어감
		return -1;
	// for (int k = 0; k < 5; k++)
	while(!done)
	{

		fetch();
		decode();
		exe();
		mem();
		wb();


		cycles++;    //increase clock cycle

		//if debug mode, print clock cycle, pc, reg 
		if (*av[2] == '0') {
			print_cycles();  //print clock cycles
			print_pc();		 //print pc
			print_reg();	 //print registers
		}

		// check the exit condition, do not delete!! 
		if (regs[9] == 10)  //if value in $t1 is 10, finish the simulation
			done = 1;
	}

	if (*av[2] == '1')
	{
		print_cycles();  //print clock cycles
		print_pc();		 //print pc
		print_reg();	 //print registers
	}

	return 0;
}


/* initialize all datapat elements
//fill the instruction and data memory
//reset the registers
*/
int init(char* filename)
{
	FILE* fp = fopen(filename, "r");
	int i;
	long inst;

	if (fp == NULL)
	{
		fprintf(stderr, "Error opening file.\n");
		return -1;
	}

	/* fill instruction memory */
	i = 0;
	while (fscanf(fp, "%lx", &inst) == 1)
	{
		inst_mem[i++] = inst;		// 10 진수로 저장됨
	}


	/*reset the registers*/
	for (i = 0; i < 32; i++)
	{
		regs[i] = 0;
	}

	/*reset pc*/
	pc = 0;
	/*reset clock cycles*/
	cycles = 0;
	return 0;
}

void print_cycles()
{
	printf("---------------------------------------------------\n");

	printf("Clock cycles = %lld\n", cycles);
}

void print_pc()
{
	printf("PC	   = %ld\n\n", pc);
}

void print_reg()
{
	printf("x0   = %d\n", regs[0]);
	printf("x1   = %d\n", regs[1]);
	printf("x2   = %d\n", regs[2]);
	printf("x3   = %d\n", regs[3]);
	printf("x4   = %d\n", regs[4]);
	printf("x5   = %d\n", regs[5]);
	printf("x6   = %d\n", regs[6]);
	printf("x7   = %d\n", regs[7]);
	printf("x8   = %d\n", regs[8]);
	printf("x9   = %d\n", regs[9]);
	printf("x10  = %d\n", regs[10]);
	printf("x11  = %d\n", regs[11]);
	printf("x12  = %d\n", regs[12]);
	printf("x13  = %d\n", regs[13]);
	printf("x14  = %d\n", regs[14]);
	printf("x15  = %d\n", regs[15]);
	printf("x16  = %d\n", regs[16]);
	printf("x17  = %d\n", regs[17]);
	printf("x18  = %d\n", regs[18]);
	printf("x19  = %d\n", regs[19]);
	printf("x20  = %d\n", regs[20]);
	printf("x21  = %d\n", regs[21]);
	printf("x22  = %d\n", regs[22]);
	printf("x23  = %d\n", regs[23]);
	printf("x24  = %d\n", regs[24]);
	printf("x25  = %d\n", regs[25]);
	printf("x26  = %d\n", regs[26]);
	printf("x27  = %d\n", regs[27]);
	printf("x28  = %d\n", regs[28]);
	printf("x29  = %d\n", regs[29]);
	printf("x30  = %d\n", regs[30]);
	printf("x31  = %d\n", regs[31]);
	printf("\n");
}