Added code to record statistics and a tool to plot them

This commit is contained in:
(Tim) Efthimis Kritikos 2023-05-14 16:06:33 +01:00
parent df342467c7
commit 07d2a80b2e
11 changed files with 308 additions and 24 deletions

2
.gitignore vendored
View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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}"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

56
tools/plot.sh Executable file
View File

@ -0,0 +1,56 @@
#!/bin/sh
set -euf
print_help(){
echo "$0 <json data file> <function> <output filename>"
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