apex_cpu_pipeline_simulator/apex_cpu.c
/*
* apex_cpu.c
* Contains APEX cpu pipeline implementation
*
* Author:
* Copyright (c) 2020, Gaurav Kothari ([email protected])
* State University of New York at Binghamton
*/
#include
#include
#include
#include "apex_cpu.h"
#include "apex_macros.h"
extern int isForwarding;
extern int isDisplay;
extern int numOfCycles;
/* Converts the PC(4000 series) into array index for code memory
*
* Note: You are not supposed to edit this function
*/
static int
get_code_memory_index_from_pc(const int pc)
{
return (pc - 4000) / 4;
}
static void
print_instruction(const CPU_Stage *stage)
{
switch (stage->opcode)
{
case OPCODE_ADD:
case OPCODE_SUB:
case OPCODE_MUL:
case OPCODE_DIV:
case OPCODE_AND:
case OPCODE_OR:
case OPCODE_XOR:
{
printf("%s,R%d,R%d,R%d ", stage->opcode_str, stage->rd, stage->rs1,
stage->rs2);
break;
}
case OPCODE_MOVC:
{
printf("%s,R%d,#%d ", stage->opcode_str, stage->rd, stage->imm);
break;
}
case OPCODE_LOAD:
{
printf("%s,R%d,R%d,#%d ", stage->opcode_str, stage->rd, stage->rs1,
stage->imm);
break;
}
case OPCODE_STORE:
{
printf("%s,R%d,R%d,#%d ", stage->opcode_str, stage->rs1, stage->rs2,
stage->imm);
break;
}
case OPCODE_BZ:
case OPCODE_BNZ:
{
printf("%s,#%d ", stage->opcode_str, stage->imm);
break;
}
case OPCODE_HALT:
{
printf("%s", stage->opcode_str);
break;
}
case OPCODE_ADDL:
case OPCODE_SUBL:
{
printf("%s,R%d,R%d,#%d ", stage->opcode_str, stage->rd, stage->rs1,
stage->imm);
break;
}
case OPCODE_STR:
{
printf("%s,R%d,R%d,R%d ", stage->opcode_str, stage->rd, stage->rs1,
stage->rs2);
break;
}
case OPCODE_LDR:
{
printf("%s,R%d,R%d,#%d ", stage->opcode_str, stage->rd, stage->rs1,
stage->rs2);
break;
}
case OPCODE_CMP:
{
printf("%s,R%d,R%d", stage->opcode_str, stage->rs1, stage->rs2);
break;
}
case OPCODE_NOP:
{
printf("%s", stage->opcode_str);
break;
}
}
}
/* Debug function which prints the CPU stage content
*
* Note: You can edit this function to print in more detail
*/
static void
print_stage_content(const char *name, const CPU_Stage *stage)
{
if (isDisplay)
{
printf("Instruction at ");
printf("%-35s (I%d: %d) ", name, get_code_memory_index_from_pc(stage->pc), stage->pc);
print_instruction(stage);
printf("\n");
}
}
static void
print_stage_content_empty(const char *name, const char *name2)
{
if (isDisplay)
{
printf("Instruction at ");
printf("%-35s %s", name, name2);
//printf("%-15s--> (I%d: %d) ", name, get_code_memory_index_from_pc(stage->pc),stage->pc);
//print_instruction(stage);
printf("\n");
}
}
/* Debug function which prints the register file
*
* Note: You are not supposed to edit this function
*/
static void
print_reg_file(const APEX_CPU *cpu)
{
//int i;
//printf("----------\n%s\n----------\n", "Registers:");
printf("\n");
printf("\n");
printf("--------------------------------");
printf(" STATE OF ARCHITECTURAL REGISTER FILE");
printf(" ----------------------------------------\n");
printf("\n");
printf("\n");
for (int i = 0; i < REG_FILE_SIZE; ++i)
{
printf("REG[%-3d%s ", i, "]");
printf(" value = %-4d", cpu->regs[i]);
if(cpu->reg_values[i] == 0){
printf(" Status = %-4s", "NOT VALID");
}else{
printf(" Status = %-4s", "VALID");
}
printf("\n");
}
printf("\n");
printf("\n");
printf("\n");
printf("--------------------------------");
printf("STATE OF DATA MEMORY");
printf(" ----------------------------------------\n");
for (int i = 0; i < 100; ++i)
{
printf("MEM[%-3d%s ", i, "]");
printf(" DATA VALUE = %-4d", cpu->data_memory[i]);
printf("\n");
}
// for (i = (REG_FILE_SIZE / 2); i < REG_FILE_SIZE; ++i)
// {
// printf("R%-3d[%-3d] ", i, cpu->regs[i]);
// }
printf("\n");
}
/*
* Fetch Stage of APEX Pipeline
*
* Note: You are free to edit this function according to your implementation
*/
static void
APEX_fetch(APEX_CPU *cpu)
{
APEX_Instruction *current_ins;
if (cpu->fetch.has_insn)
{
/* This fetches new branch target instruction from next cycle */
if (cpu->fetch_from_next_cycle == TRUE || cpu->fetch_stall == TRUE)
{
cpu->fetch_from_next_cycle = FALSE;
cpu->fetch.pc = cpu->pc;
current_ins = &cpu->code_memory[get_code_memory_index_from_pc(cpu->pc)];
strcpy(cpu->fetch.opcode_str, current_ins->opcode_str);
cpu->fetch.opcode = current_ins->opcode;
cpu->fetch.rd = current_ins->rd;
cpu->fetch.rs1 = current_ins->rs1;
cpu->fetch.rs2 = current_ins->rs2;
cpu->fetch.imm = current_ins->imm;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Fetch", &cpu->fetch);
}
/* Skip this cycle*/
return;
}
/* Store current PC in fetch latch */
cpu->fetch.pc = cpu->pc;
/* Index into code memory using this pc and copy all instruction fields
* into fetch latch */
current_ins = &cpu->code_memory[get_code_memory_index_from_pc(cpu->pc)];
strcpy(cpu->fetch.opcode_str, current_ins->opcode_str);
cpu->fetch.opcode = current_ins->opcode;
cpu->fetch.rd = current_ins->rd;
cpu->fetch.rs1 = current_ins->rs1;
cpu->fetch.rs2 = current_ins->rs2;
cpu->fetch.imm = current_ins->imm;
/* Update PC for next instruction */
cpu->pc += 4;
/* Copy data from fetch latch to decode latch*/
cpu->decode = cpu->fetch;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Fetch", &cpu->fetch);
}
/* Stop fetching new instructions if HALT is fetched */
if (cpu->fetch.opcode == OPCODE_HALT)
{
cpu->fetch.has_insn = FALSE;
}
}
else
{
print_stage_content_empty("Fetch", "EMPTY");
}
}
/*
* Decode Stage of APEX Pipeline
*
* Note: You are free to edit this function according to your implementation
*/
static void
APEX_decode(APEX_CPU *cpu)
{
if (cpu->decode.has_insn)
{
/* Read operands from register file based on the instruction type */
switch (cpu->decode.opcode)
{
case OPCODE_ADD:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_ADDL:
{
if (cpu->reg_values[cpu->decode.rs1] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
// cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_SUBL:
{
if (cpu->reg_values[cpu->decode.rs1] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
// cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_STORE:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->fetch_stall = FALSE;
// return;
}
//cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
break;
}
case OPCODE_LOAD:
{
if (cpu->reg_values[cpu->decode.rs1] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
break;
}
case OPCODE_MOVC:
{
/* MOVC doesn't have register operands */
break;
}
case OPCODE_MUL:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_SUB:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_DIV:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_AND:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_OR:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_XOR:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_CMP:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_LDR:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->fetch_stall = FALSE;
// return;
}
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
break;
}
case OPCODE_STR:
{
if (cpu->reg_values[cpu->decode.rs1] == 0 || cpu->reg_values[cpu->decode.rs2] == 0 || cpu->reg_values[cpu->decode.rd] == 0)
{
cpu->fetch_stall = TRUE;
if (ENABLE_DEBUG_MESSAGES)
{
print_stage_content("Decode/RF", &cpu->decode);
}
return;
}
else
{
cpu->decode.rs2_value = cpu->regs[cpu->decode.rs2];
cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
cpu->decode.result_buffer = cpu->regs[cpu->decode.rd];
cpu->fetch_stall = FALSE;
// return;
}
//cpu->decode.rs1_value = cpu->regs[cpu->decode.rs1];
break;
}
}
/* Copy data from decode latch to execute latch*/
cpu->execute = cpu->decode;
cpu->decode.has_insn = FALSE;
if (ENABLE_DEBUG_MESSAGES)
...