From 07d2a80b2e0fbedcf145bd3cd7b9ce50b5d72f47 Mon Sep 17 00:00:00 2001 From: "(Tim) Efthimis Kritikos" Date: Sun, 14 May 2023 16:06:33 +0100 Subject: [PATCH] Added code to record statistics and a tool to plot them --- .gitignore | 2 + boot_code/Makefile | 3 +- boot_code/pipeline_ideal.asm | 106 +++++++++++++++++++++++++++++++++++ common.mk | 7 ++- system/Makefile | 6 +- system/biu.v | 16 ++++-- system/config.v | 9 +++ system/processor.v | 20 ++++++- system/system.v | 81 +++++++++++++++++++++++--- system/testbench.v | 26 +++++++-- tools/plot.sh | 56 ++++++++++++++++++ 11 files changed, 308 insertions(+), 24 deletions(-) create mode 100644 boot_code/pipeline_ideal.asm create mode 100755 tools/plot.sh diff --git a/.gitignore b/.gitignore index 73d0dad..7b29c30 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.bf.asm *.swp *.memdump +*.json boot_code/brainfuck_interpreted.bin boot_code/brainfuck_interpreted.txt boot_code/brainfuck_mandelbrot.bin @@ -14,3 +15,4 @@ boot_code/brainfuck_compiled.txt system/boot_code.bin system/boot_code.txt system/obj_dir/ +tools/*svg diff --git a/boot_code/Makefile b/boot_code/Makefile index 9f714d2..c28dbc4 100644 --- a/boot_code/Makefile +++ b/boot_code/Makefile @@ -1,9 +1,10 @@ -SOURCE=brainfuck_interpreted.asm brainfuck_compiled.asm brainfuck_mandelbrot.asm +SOURCE=brainfuck_interpreted.asm brainfuck_compiled.asm brainfuck_mandelbrot.asm pipeline_ideal.asm BINARIES=$(subst .asm,.txt,${SOURCE}) BUILD_FILES=${BINARIES} BUILD_FILES+=$(subst .asm,.memdump,${SOURCE}) BUILD_FILES+=$(subst .asm,.fst,${SOURCE}) BUILD_FILES+=$(subst .asm,.bin,${SOURCE}) +BUILD_FILES+=$(subst .asm,.json,${SOURCE}) all: ${BINARIES} diff --git a/boot_code/pipeline_ideal.asm b/boot_code/pipeline_ideal.asm new file mode 100644 index 0000000..6ef3a20 --- /dev/null +++ b/boot_code/pipeline_ideal.asm @@ -0,0 +1,106 @@ +org 0x100 +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +inc ax +inc bx +hlt + +.ORG 0xFFF0 +MOV AX,#0x0100 +JMP AX diff --git a/common.mk b/common.mk index d3d7f30..9f00b39 100644 --- a/common.mk +++ b/common.mk @@ -32,7 +32,6 @@ run: $(subst .txt,.run,${BOOT_CODE}) wave: $(subst .txt,.wave,${BOOT_CODE}) disas: $(subst .txt,.disas,${BOOT_CODE}) - # Assembling code %.txt:%.bin ${Q}dd if=/dev/zero bs=1 count=65536 of="$(subst .bin,.stage,$<)" status=none @@ -57,6 +56,9 @@ ifeq "${SIM}" "ICARUS" %.run: %.txt ${SYSTEM_VVP} ${MICROCODE} ${QUIET_VVP} ${Q}vvp -i "${SYSTEM_VVP}" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}" +%.json: %.txt ${SYSTEM_VVP} ${MICROCODE} + ${QUIET_VVP} + ${Q}vvp -i "${SYSTEM_VVP}" +STATS="$@" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}" else ifeq "${SIM}" "VERILATOR" %.fst %.memdump: %.txt ${VERILATOR_BIN} ${MICROCODE} $(call QUIET_VERILATOR_RUN,$(word 2,$^),$<) @@ -64,6 +66,9 @@ else ifeq "${SIM}" "VERILATOR" ${Q}grep -v '^//' "$(subst .txt,.memdumptxt,$<)" | xxd -ps -c 2 -r > "$(subst .txt,.memdump,$<)" ${Q}rm "$(subst .txt,.memdumptxt,$<)" +%.json: %.txt ${VERILATOR_BIN} ${MICROCODE} + $(call QUIET_VERILATOR_RUN,$(word 2,$^),$<) + ${Q} ${NUMACTL} "${VERILATOR_BIN}" +STATS=$@ +BOOT_CODE="$<" +MICROCODE="${MICROCODE}" %.run: %.txt ${VERILATOR_BIN} ${MICROCODE} $(call QUIET_VERILATOR_RUN,$(word 2,$^),$<) ${Q} ${NUMACTL} "${VERILATOR_BIN}" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}" diff --git a/system/Makefile b/system/Makefile index 5ac9085..0ed037c 100644 --- a/system/Makefile +++ b/system/Makefile @@ -46,16 +46,16 @@ VERILATOR_OPTS += -x-assign fast --x-initial fast # COMPILING ${SYSTEM_VVP} : ${TOP_LEVEL_SOURCE} ${SOURCES} ${INCLUDES} ${EVENT_SIM_TESTBENCH} ${QUIET_IVERILOG} - ${Q}iverilog -g2012 -o "$@" ${TOP_LEVEL_SOURCE} ${SOURCES} ${EVENT_SIM_TESTBENCH} + ${Q}iverilog -g2012 -D CALCULATE_IPC -D OTUPUT_JSON_STATISTICS -o "$@" ${TOP_LEVEL_SOURCE} ${SOURCES} ${EVENT_SIM_TESTBENCH} ${VERILATOR_BIN}: ${VERILATOR_BIN}.mk ${Q}make ${MAKEOPTS} OPT_FAST="-O2 -march=native -mtune=native" -C obj_dir -f ../verilator_makefile Vsystem ${VERILATOR_BIN}.mk: ${VERILATOR_TESTBENCH} ${TOP_LEVEL_SOURCE} ${SOURCES} ${INCLUDES} ${QUIET_VERILATOR} - ${Q}verilator ${VERILATOR_OPTS} $^ + ${Q}verilator -DCALCULATE_IPC -DOTUPUT_JSON_STATISTICS ${VERILATOR_OPTS} $^ .PHONY: clean clean: $(call QUIET_CLEAN,system) - ${Q}rm -rf ${SYSTEM_VVP} *.fst boot_code.txt boot_code.bin *memdump *memdumptxt obj_dir + ${Q}rm -rf ${SYSTEM_VVP} *.fst boot_code.txt boot_code.bin *memdump *memdumptxt obj_dir *json diff --git a/system/biu.v b/system/biu.v index 50fec42..7e6c5e3 100644 --- a/system/biu.v +++ b/system/biu.v @@ -43,8 +43,16 @@ module BIU ( /* Internal */ output reg [31:0] INSTRUCTION, output reg VALID_INSTRUCTION, output reg [15:0] INSTRUCTION_LOCATION, input jump_req, /* */ input[15:0] ADDRESS_INPUT, inout [15:0] DATA, input write_request, input read_request, input Wbit, output reg VALID_DATA, input MEM_OR_IO, /* */ input [`PROC_STATE_BITS-1:0] proc_state, input SIMPLE_MICRO +`ifdef OTUPUT_JSON_STATISTICS +/* Statistics */ ,output wire [`L1_CACHE_SIZE-1:0] FIFO_SIZE_STAT, output wire VALID_INSTRUCTION_STAT +`endif ); +`ifdef OTUPUT_JSON_STATISTICS +assign FIFO_SIZE_STAT = FIFO_SIZE; +assign VALID_INSTRUCTION_STAT = ((Isit1==1) && (FIFO_SIZE!=0) && `EARLY_VALID_INSTRUCTION_) || ((fifoIsize==2) && (FIFO_SIZE > 1) && `EARLY_VALID_INSTRUCTION_) || ((fifoIsize==3) && (FIFO_SIZE > 2) && `EARLY_VALID_INSTRUCTION_) || (FIFO_SIZE>3); +`endif + reg [15:0] data_bus_output_register; assign external_data_bus=read?data_bus_output_register:16'hz; @@ -153,11 +161,11 @@ always @(posedge clock) begin end else if(INSTRUCTION_ADDRESS[0:0]==0)begin INPUT_FIFO[FIFO_end] <= external_data_bus[7:0]; FIFO_end <= FIFO_end+4'd1; - INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+1; + INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+20'd1; end else begin INPUT_FIFO[FIFO_end] <= external_data_bus[15:8]; FIFO_end <= FIFO_end+4'd1; - INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+1; + INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+20'd1; end biu_state <= `BIU_NEXT_ACTION; read<=1; @@ -176,7 +184,7 @@ always @(posedge clock) begin end `BIU_PUT_UNALIGNED_PREP_NEXT2:begin write <= 1; - DATA_ADDRESS <= DATA_ADDRESS+1; + DATA_ADDRESS <= DATA_ADDRESS+20'd1; BHE <= 1; biu_state <= `BIU_WRITE_EXIT; end @@ -245,7 +253,7 @@ always @(posedge clock) begin end end `BIU_GET_SECOND_BYTE:begin - DATA_ADDRESS <= DATA_ADDRESS+1; + DATA_ADDRESS <= DATA_ADDRESS+20'd1; biu_state <= `BIU_GET_SECOND_BYTE1; read <=0; end diff --git a/system/config.v b/system/config.v index 3e17d9c..57c8cbd 100644 --- a/system/config.v +++ b/system/config.v @@ -22,6 +22,11 @@ //`define DEBUG_PC_ADDRESS //`define DEBUG_DATA_READ_WRITES +/** OUTPUT **/ +/* These are usually set at build time*/ +//`define CALCULATE_IPC +//`define OTUPUT_JSON_STATISTICS + /** Optimisations **/ /* Enables the ability to check if an instruction in the input @@ -44,3 +49,7 @@ `else `define EARLY_VALID_INSTRUCTION_ 0 `endif + +`ifdef OTUPUT_JSON_STATISTICS + `define CALCULATE_IPC +`endif diff --git a/system/processor.v b/system/processor.v index 3adf449..5c16933 100644 --- a/system/processor.v +++ b/system/processor.v @@ -36,7 +36,16 @@ //read: active low //reset: active low -module processor ( input clock, input reset, output [19:0] external_address_bus, inout [15:0] external_data_bus,output read, output write,output BHE,output IOMEM, output wire HALT,output [`ERROR_BITS-1:0] ERROR); +module processor ( + /* MISC */ input clock, input reset, output wire HALT,output [`ERROR_BITS-1:0] ERROR + /* MEMORY / IO */ ,output [19:0] external_address_bus, inout [15:0] external_data_bus,output read, output write,output BHE,output IOMEM +`ifdef CALCULATE_IPC + /* STATISTICS */ ,output reg new_instruction +`endif +`ifdef OTUPUT_JSON_STATISTICS + /* */ ,output wire [`L1_CACHE_SIZE-1:0] L1_SIZE_STAT, output wire VALID_INSTRUCTION_STAT +`endif + ); /* If there is an error either from the decoder or execution unit set it to ERROR */ assign ERROR=(DE_ERROR!=`ERR_NO_ERROR)?DE_ERROR:(EXEC_ERROR!=`ERR_NO_ERROR)?EXEC_ERROR:`ERR_NO_ERROR; @@ -99,6 +108,9 @@ BIU BIU( /* Internal */ ,INSTRUCTION,VALID_INSTRUCTION,INSTRUCTION_LOCATION,biu_jump_req /* */ ,BIU_ADDRESS_INPUT,BIU_DATA,biu_write_request,biu_read_request,Wbit,BIU_VALID_DATA,MEM_OR_IO /* */ ,state,SIMPLE_MICRO +`ifdef OTUPUT_JSON_STATISTICS + /* Statistics */ ,L1_SIZE_STAT, VALID_INSTRUCTION_STAT +`endif ); assign BIU_DATA= biu_data_direction ? 16'hz : (memio_address_select ? reg_read_port1_data : ALU_O); @@ -176,6 +188,9 @@ register_file register_file( always @(negedge reset) begin state <= `PROC_HALT; //TODO: race condition ?? + `ifdef CALCULATE_IPC + new_instruction<=0; + `endif end always @(posedge reset) begin state <= `PROC_RESET; @@ -211,6 +226,9 @@ always @(posedge clock) begin `ifdef DEBUG_PC_ADDRESS $display("Running command at %04x (%08x)",INSTRUCTION_LOCATION,INSTRUCTION); `endif + `ifdef CALCULATE_IPC + new_instruction <= !new_instruction; + `endif ProgCount <= INSTRUCTION_LOCATION+{12'b0,instr_end}; INSTRUCTION_BUFFER<=INSTRUCTION[23:0]; end diff --git a/system/system.v b/system/system.v index 7bea1f1..85255e3 100644 --- a/system/system.v +++ b/system/system.v @@ -19,19 +19,51 @@ `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); -processor p(clock,reset,address_bus,data_bus,rd,wr,BHE,IOMEM,HALT,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; +`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 +`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; @@ -45,16 +77,32 @@ end // 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}",first_json_cycle?"":",",cycles,L1_SIZE_STAT,VALID_INSTRUCTION_STAT); + 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 -reg [1:0] finish; +`ifdef CALCULATE_IPC +integer instruction_count; +always @(new_instruction) begin + instruction_count<=instruction_count+1; +end +`endif + +reg [1:0] finish; string memdump_name; always @(posedge HALT) begin - $display("Processor halted.\nCycles run for: %d",cycles-1); if($value$plusargs("MEMDUMP=%s",memdump_name))begin $writememh(memdump_name, sysmem.memory,0,32767); end @@ -65,7 +113,20 @@ always @(posedge clock) begin /* Allow some clock cycles for the waveform*/ case(finish) 2'd0: begin end - 2'd1: finish <= 2; + 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 @@ -84,7 +145,7 @@ always @( ERROR ) begin $display("Unimplemented addressing mode"); end endcase - $display("Cycles run for: %d",cycles-1); + $display("Cycles run for: %0d",cycles-1); if($value$plusargs("MEMDUMP=%s",memdump_name))begin $writememh(memdump_name, system.sysmem.memory,0,32767); end @@ -92,13 +153,17 @@ always @( ERROR ) begin end end -integer cycles=0; +integer cycles; -always @(posedge clock)begin +always @(negedge clock)begin if(reset==1) cycles<=cycles+1; - else + else begin cycles<=0; + `ifdef OTUPUT_JSON_STATISTICS + instruction_count <= 0; + `endif + end end diff --git a/system/testbench.v b/system/testbench.v index aec22e8..12ba661 100644 --- a/system/testbench.v +++ b/system/testbench.v @@ -46,13 +46,27 @@ clock_gen #(.FREQ(1000)) u1(clk_enable, clock); string memdump_name; initial begin clk_enable = 1; + do_reset = 0; +end - reset = 1; - #(`CPU_SPEED*2) - reset = 0; - #($random%1000) - #(`CPU_SPEED) - reset = 1; +reg [1:0]do_reset; + +always @(posedge clock) begin + case(do_reset) + 2'd0:begin + do_reset<=1; + end + 2'd1:begin + do_reset<=2; + reset <= 0; + end + 2'd2:begin + do_reset<=3; + reset <= 1; + end + 2'd3:begin + end + endcase end endmodule diff --git a/tools/plot.sh b/tools/plot.sh new file mode 100755 index 0000000..b2a1302 --- /dev/null +++ b/tools/plot.sh @@ -0,0 +1,56 @@ +#!/bin/sh +set -euf + +print_help(){ + echo "$0 " + echo " Possible functions:" + echo " cache_time : graph of cache utilisation in bytes over time in clock cycles" + echo " cache_util_freq : graph of the likelyhood of the cache for each utilisation" +} + +if [ "$#" != 3 ] || ! [ -e "$1" ] +then + print_help + exit 1 +fi + +CSV_FILE=$(mktemp) +IN=$1 +OUT=$3 + +jq -r '.Cycles[]| [.C,.L1,.VDI]|@csv' -- "$IN" > "$CSV_FILE" + +BASE_GNUPLOT_OPTIONS="set datafile separator ',';set term svg;set output \"${OUT}\";" + +case "$2" in + "cache_time") + CACHE_SIZE=$(jq -r .L1_size -- "$IN") + gnuplot -e "${BASE_GNUPLOT_OPTIONS}\ +set xlabel \"Clock cycles\";\ +set ylabel \"Cache utilisation (bytes)\";\ +set offsets 0, 0, 3, 1;\ +set ytics 0,1,$((CACHE_SIZE-1));\ +unset colorbox;\ +set ytics add (\"Valid\" -1);\ +set palette model RGB defined ( 0 'red', 1 'green' );\ +plot '${CSV_FILE}' using 1:2 with lines notitle ,\ + '${CSV_FILE}' using 1:(-0.75):(0):(-0.5):3 with vectors nohead palette notitle" + + ;; + "cache_util_freq") + CACHE_SIZE=$(jq -r .L1_size -- "$IN") + gnuplot -e "${BASE_GNUPLOT_OPTIONS}\ +set xlabel \"Utilisation (bytes)\";\ +set ylabel \"Occurrences\";\ +set boxwidth 0.5;\ +set style fill solid 0.4;\ +set xtics 0,1,$((CACHE_SIZE-1));\ +set offsets 1, 2, 0, 0;\ + plot '${CSV_FILE}' using 2:(1) smooth frequency with boxes notitle" + + ;; + *) + print_help; + exit 1 + ;; +esac