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

   This file is part of the 9086 project.

   Copyright (c) 2024 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, output [15:0]data_bus_write ,output BHE, output rd, output wr, output IOMEM, output HALT, output [`ERROR_BITS-1:0] ERROR);

`ifdef CALCULATE_IPC
wire new_instruction;
`endif

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

wire [15:0]data_bus_read_CPU,data_bus_write_CPU;

assign data_bus_read_CPU=(IOMEM==0)?data_bus_read_RAM:data_bus_IO;
assign data_bus_write=data_bus_write_CPU;
wire wait_state=1'b0;

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

wire [15:0] data_bus_read_RAM;

doublemem #(.RAM_SIZE_IN_BYTES(65536)) sysmem(address_bus[15:0],data_bus_read_RAM,data_bus_write_CPU,rd,wr,BHE,IOMEM,clock);

`ifdef OUTPUT_JSON_STATISTICS
string stats_name,version,commit;
integer json_file_descriptor;
`endif

`ifndef YOSYS
string waveform_name;
`endif

initial begin
	`ifndef SYNTHESIS
		if($value$plusargs("WAVEFORM=%s",waveform_name))begin
			$dumpfile(waveform_name);
			$dumpvars(0,p,cycles);
		end
		`ifdef OUTPUT_JSON_STATISTICS
			if(!$value$plusargs("VERSION=%s",version)) version="unkown";
			if(!$value$plusargs("COMMIT=%s",commit)) commit="unkown";
			if($value$plusargs("STATS=%s",stats_name))begin
				json_file_descriptor=$fopen(stats_name,"w");
				$fdisplay(json_file_descriptor,"{\n\"L1_size\":%0d,\n\"9086 verison\":\"%s\",\n\"latest commit\":\"%s\",\n\"Cycles\":[",$rtoi($pow(2,`L1_CACHE_SIZE)),version,commit);
				first_json_cycle = 1;
			end else
				json_file_descriptor=0;
		`endif
		sane=0;
		finish=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 OUTPUT_JSON_STATISTICS
reg first_json_cycle;
always @(negedge clock)begin
	if(finish < 2 && json_file_descriptor!=0 && sane)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

reg [15:0]data_bus_IO;

`ifndef SYNTHESIS
always @(negedge wr) begin
	if(IOMEM==1'b1 && address_bus[7:0]==8'hA5 )begin
		$write("%s" ,data_bus_write[15:8]);
	end else if(IOMEM==1'b1 && address_bus[7:0]==8'hB0 )begin
		if(data_bus_write[0:0]==1)
			$display("\x1b[7mLed turned on\x1b[m\n");
		else
			$display("\x1b[7mLed turned off\x1b[m\n");
	end
end
always @(negedge rd) begin
	if(IOMEM==1'b1 && address_bus[7:1]==7'h10 )begin // 0xABCD on address 0x20
		data_bus_IO<=16'hABCD;
	end else begin
		data_bus_IO<=16'h0000;
	end
end
`endif


`ifdef CALCULATE_IPC
/* verilator lint_off MULTIDRIVEN */
reg [128:0] instruction_count;
/* verilator lint_on MULTIDRIVEN */
always @(new_instruction) begin
	instruction_count<=instruction_count+1;
end
`endif


`ifndef SYNTHESIS

	`ifdef OUTPUT_JSON_STATISTICS
	reg [128:0] instruction_count_temp;
	`endif

	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

	/* verilator lint_off MULTIDRIVEN */
	reg [1:0] finish;
	/* verilator lint_on MULTIDRIVEN */

	reg sane;
	reg [128:0] cycles;

	always @(posedge reset)begin
		sane<=1;
	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
					$display("\x1b[7mInstr. per cycle : %f\x1b[m", $itor(instruction_count) / $itor(cycles) );
				`endif
				`ifdef OUTPUT_JSON_STATISTICS
					instruction_count_temp <= instruction_count;
				`endif
			end
			2'd2: begin
				finish <= 3;
				`ifdef OUTPUT_JSON_STATISTICS
					if(json_file_descriptor!=0)
						$fdisplay(json_file_descriptor,"],\n\"Total Cycles\":%0d,\n\"Instructions run\":%0d\n}",cycles-1,instruction_count_temp);
				`endif
				$finish;
			end
			2'd3: begin
			end
		endcase
	end

	always @( ERROR ) begin
		if ( ERROR != `ERR_NO_ERROR && sane == 1 ) 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

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

`endif



endmodule
