/* system.v - A basic test system with memory and IO for the 9086 CPU

   This file is part of the 9086 project.

   Copyright (c) 2023 Efthymios Kritikos

   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/>.  */

`timescale 1ns/1ps
`include "error_header.v"
`include "config.v"


module system ( input clock,input reset, output [19:0]address_bus, inout [15:0]data_bus,output BHE, output rd, output wr, output IOMEM, output HALT, output [`ERROR_BITS-1:0] ERROR);

`ifdef CALCULATE_IPC
wire new_instruction;
`endif

`ifdef OTUPUT_JSON_STATISTICS
wire unsigned [`L1_CACHE_SIZE-1:0] L1_SIZE_STAT;
wire VALID_INSTRUCTION_STAT,jump_req;
`endif

processor p(
	/*     MISC    */ clock,reset,HALT,ERROR
	/* MEMORY / IO */ ,address_bus,data_bus,rd,wr,BHE,IOMEM
`ifdef CALCULATE_IPC
	/*  STATISTICS */ ,new_instruction
`endif
`ifdef OTUPUT_JSON_STATISTICS
	/*             */ ,L1_SIZE_STAT, VALID_INSTRUCTION_STAT, jump_req
`endif
	);

doublemem sysmem(address_bus,data_bus,rd,wr,BHE,IOMEM);

`ifdef OTUPUT_JSON_STATISTICS
string stats_name;
integer json_file_descriptor;
`endif
string waveform_name;
initial begin
	if($value$plusargs("WAVEFORM=%s",waveform_name))begin
		$dumpfile(waveform_name);
		$dumpvars(0,p);
	end
	`ifdef OTUPUT_JSON_STATISTICS
	if($value$plusargs("STATS=%s",stats_name))begin
		json_file_descriptor=$fopen(stats_name,"w");
		$fdisplay(json_file_descriptor,"{\n\"L1_size\":%0d,\n\"Cycles\":[",$rtoi($pow(2,`L1_CACHE_SIZE)));
		first_json_cycle = 1;
	end else
		json_file_descriptor=0;
	`endif
end

//integer killswitch=0;
//always @(posedge clock) begin
// killswitch <= killswitch +1;
// if( killswitch == 20000 )begin
//	if($value$plusargs("MEMDUMP=%s",memdump_name))begin
//		$writememh(memdump_name, system.sysmem.memory,0,32767);
//	end
// 	$finish;
//	end
//end

`ifdef OTUPUT_JSON_STATISTICS
reg first_json_cycle;
always @(negedge clock)begin
	if(HALT==0 && json_file_descriptor!=0)begin
		$fdisplay(json_file_descriptor,"%s{\"C\":%0d,\"L1\":%0d,\"VDI\":%0d,\"JMP\":%0d}",first_json_cycle?"":",",cycles,L1_SIZE_STAT,VALID_INSTRUCTION_STAT,jump_req);
		first_json_cycle <= 0;
	end
end
`endif

always @(negedge wr) begin
	if(IOMEM==1'b1 && address_bus[7:0]==8'hA5 )
		$write("%s" ,data_bus[15:8]);
end


`ifdef CALCULATE_IPC
reg [128:0] instruction_count;
always @(new_instruction) begin
	instruction_count<=instruction_count+1;
end
`endif

reg [1:0] finish;
string memdump_name;
always @(posedge HALT) begin
	if($value$plusargs("MEMDUMP=%s",memdump_name))begin
		$writememh(memdump_name, sysmem.memory,0,32767);
	end
	finish<=2'd1;
end

always @(posedge clock) begin
	/* Allow some clock cycles for the waveform*/
	case(finish)
		2'd0: begin end
		2'd1: begin
			finish <= 2;
			/* instruction_count gets updated at the sme time as HALT is pulled so wait a clock cycle to get an accurate reading*/
			$display("\x1b[7mProcessor halted.\nCycles run for   : %0d\x1b[m",cycles);
			`ifdef CALCULATE_IPC
			/* verilator lint_off REALCVT */
				$display("\x1b[7mInstr. per cycle : %f\x1b[m", $itor(instruction_count) / $itor(cycles) );
				/* verilator lint_on REALCVT */
			`endif
			`ifdef OTUPUT_JSON_STATISTICS
				if(json_file_descriptor!=0)
					$fdisplay(json_file_descriptor,"],\n\"Total Cycles\":%0d,\n\"Instructions run\":%0d\n}",cycles,instruction_count);
			`endif
		end
		2'd2: finish <= 3;
		2'd3: $finish;
	endcase
end

always @( ERROR ) begin
	if ( ERROR != `ERR_NO_ERROR ) begin
		$display("PROCESSOR RUN INTO AN ERROR.");
		case (ERROR)
			default:begin
			end
			`ERR_UNIMPL_INSTRUCTION:begin
				$display("Unimplemented instruction");
			end
			`ERR_UNIMPL_ADDRESSING_MODE: begin
				$display("Unimplemented addressing mode");
			end
		endcase
		$display("Cycles run for: %0d",cycles-1);
		if($value$plusargs("MEMDUMP=%s",memdump_name))begin
			$writememh(memdump_name, system.sysmem.memory,0,32767);
		end
		finish<=2'd1;
	end
end

reg [128:0] cycles;

always @(negedge clock)begin
	if(reset==1)
		cycles<=cycles+1;
	else begin
		cycles<=0;
		`ifdef OTUPUT_JSON_STATISTICS
		instruction_count <= 0;
		`endif
	end
end


endmodule