Skip to content

Commit d2a0024

Browse files
riscv 3 cycle design fix to ensure certain stages read from registers and not comb same cycle next signal. also improves mem map wiring with new version of macros used
1 parent b08e21f commit d2a0024

File tree

2 files changed

+77
-53
lines changed

2 files changed

+77
-53
lines changed

examples/risc-v/multi_cycle_risc-v.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#pragma PART "xc7a35ticsg324-1l"
1+
#pragma PART "xc7a35ticsg324-1l" //LFE5U-85F-6BG381C" //xc7a35ticsg324-1l"
22
#include "uintN_t.h"
33
#include "intN_t.h"
44

@@ -17,8 +17,8 @@ typedef struct my_mmio_in_t{
1717
uint1_t button;
1818
}my_mmio_in_t;
1919
typedef struct my_mmio_out_t{
20-
uint32_t return_value;
21-
uint1_t halt;
20+
//uint32_t return_value;
21+
//uint1_t halt;
2222
uint1_t led;
2323
}my_mmio_out_t;
2424
// Define the hardware memory for those IO
@@ -27,13 +27,14 @@ riscv_mem_map_mod_out_t(my_mmio_out_t) my_mem_map_module(
2727
RISCV_MEM_MAP_MOD_INPUTS(my_mmio_in_t)
2828
){
2929
// Outputs
30-
static riscv_mem_map_mod_out_t(my_mmio_out_t) o;
30+
static riscv_mem_map_mod_out_t(my_mmio_out_t) o_reg;
31+
riscv_mem_map_mod_out_t(my_mmio_out_t) o = o_reg;
3132
o.addr_is_mapped = 0; // since o is static regs
3233
// Memory muxing/select logic
3334
// Uses helper comparing word address and driving a variable
34-
WORD_MM_ENTRY(o, THREAD_ID_RETURN_OUTPUT_ADDR, o.outputs.return_value)
35-
o.outputs.halt = wr_byte_ens[0] & (addr==THREAD_ID_RETURN_OUTPUT_ADDR);
36-
WORD_MM_ENTRY(o, LED_ADDR, o.outputs.led)
35+
//WORD_MM_ENTRY(o, THREAD_ID_RETURN_OUTPUT_ADDR, o.outputs.return_value)
36+
//o.outputs.halt = wr_byte_ens[0] & (addr==THREAD_ID_RETURN_OUTPUT_ADDR);
37+
WORD_MM_ENTRY_NEW(LED_ADDR, o_reg.outputs.led, o_reg.outputs.led, addr, o.addr_is_mapped, o.rd_data)
3738
return o;
3839
}
3940

@@ -59,8 +60,8 @@ MAIN_MHZ(my_top, CPU_CLK_MHZ)
5960
#include "debug_port.h"
6061
DEBUG_OUTPUT_DECL(uint1_t, unknown_op) // Unknown instruction
6162
DEBUG_OUTPUT_DECL(uint1_t, mem_out_of_range) // Exception, stop sim
62-
DEBUG_OUTPUT_DECL(uint1_t, halt) // Stop/done signal
63-
DEBUG_OUTPUT_DECL(int32_t, main_return) // Output from main()
63+
//DEBUG_OUTPUT_DECL(uint1_t, halt) // Stop/done signal
64+
//DEBUG_OUTPUT_DECL(int32_t, main_return) // Output from main()
6465

6566
void my_top()
6667
{
@@ -71,8 +72,8 @@ void my_top()
7172
// Sim debug
7273
unknown_op = out.unknown_op;
7374
mem_out_of_range = out.mem_out_of_range;
74-
halt = out.mem_map_outputs.halt;
75-
main_return = out.mem_map_outputs.return_value;
75+
//halt = out.mem_map_outputs.halt;
76+
//main_return = out.mem_map_outputs.return_value;
7677

7778
// Output LEDs for hardware debug
7879
leds = 0;

examples/risc-v/multi_cycle_risc-v_decl.h

Lines changed: 65 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -45,45 +45,58 @@ riscv_out_t riscv_name(
4545

4646
// Instead of a counter for which stage is active
4747
// one hot bit is easier to work with (especially come time for pipelining)
48-
static uint1_t stage_valid[N_CYCLES] = {[0]=1}; // Starts in stage_valid[0]
49-
uint1_t next_stage_valid[N_CYCLES]; // Update static reg at end of func
48+
static uint1_t state[N_CYCLES] = {[0]=1}; // Starts in state[0]
49+
uint1_t next_state[N_CYCLES] = state; // Update static reg at end of func
5050

5151
// CPU registers (outside of reg file)
5252
static uint32_t pc = 0;
5353
o.pc = pc; // debug
5454
// Wires/non-static local variables from original single cycle design
5555
// are likely to be shared/stored from cycle to cycle now
5656
// so just declare all (non-FEEDBACK) local variables static registers too
57-
static uint32_t pc_plus4;
58-
static riscv_imem_ram_out_t imem_out;
59-
static decoded_t decoded;
60-
static reg_file_out_t reg_file_out;
61-
static execute_t exe;
62-
static uint1_t mem_wr_byte_ens[4];
63-
static uint1_t mem_rd_byte_ens[4];
64-
static uint32_t mem_addr;
65-
static uint32_t mem_wr_data;
66-
static riscv_dmem_out_t dmem_out;
57+
static uint32_t pc_plus4_reg;
58+
static riscv_imem_ram_out_t imem_out_reg;
59+
static decoded_t decoded_reg;
60+
static reg_file_out_t reg_file_out_reg;
61+
static execute_t exe_reg;
62+
static uint1_t mem_wr_byte_ens_reg[4];
63+
static uint1_t mem_rd_byte_ens_reg[4];
64+
static uint32_t mem_addr_reg;
65+
static uint32_t mem_wr_data_reg;
66+
static riscv_dmem_out_t dmem_out_reg;
67+
// But still have non-static wires version of registers too
68+
// to help avoid/resolve unintentional passing of data between stages
69+
// in the same cycle (like single cycle cpu did)
70+
uint32_t pc_plus4 = pc_plus4_reg;
71+
riscv_imem_ram_out_t imem_out = imem_out_reg;
72+
decoded_t decoded = decoded_reg;
73+
reg_file_out_t reg_file_out = reg_file_out_reg;
74+
execute_t exe = exe_reg;
75+
uint1_t mem_wr_byte_ens[4] = mem_wr_byte_ens_reg;
76+
uint1_t mem_rd_byte_ens[4] = mem_rd_byte_ens_reg;
77+
uint32_t mem_addr = mem_addr_reg;
78+
uint32_t mem_wr_data = mem_wr_data_reg;
79+
riscv_dmem_out_t dmem_out = dmem_out_reg;
6780

6881
// Cycle 0: PC
69-
if(stage_valid[0]){
82+
if(state[0]){
7083
printf("PC in Cycle/Stage 0\n");
7184
// Program counter is input to IMEM
7285
pc_plus4 = pc + 4;
7386
printf("PC = 0x%X\n", pc);
7487
// Next state/cycle (potentially redundant)
75-
next_stage_valid = stage_valid;
76-
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
88+
next_state = state;
89+
ARRAY_1ROT_UP(uint1_t, next_state, N_CYCLES)
7790
}
7891

7992
// Boundary shared between cycle0 and cycle1
80-
if(stage_valid[0]|stage_valid[1]){
93+
if(state[0]|state[1]){
8194
// Instruction memory
8295
imem_out = riscv_imem_ram(pc>>2, 1);
8396
}
8497

8598
// Cycle1: Decode
86-
if(stage_valid[1]){
99+
if(state[1]){
87100
printf("Decode in Cycle/Stage 1\n");
88101
// Decode the instruction to control signals
89102
printf("Instruction: 0x%X\n", imem_out.rd_data1);
@@ -96,8 +109,8 @@ riscv_out_t riscv_name(
96109
}
97110
o.unknown_op = decoded.unknown_op; // debug
98111
// Next state/cycle (potentially redundant)
99-
next_stage_valid = stage_valid;
100-
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
112+
next_state = state;
113+
ARRAY_1ROT_UP(uint1_t, next_state, N_CYCLES)
101114
}
102115

103116
// Cycle1+2: Register file reads and writes
@@ -111,7 +124,7 @@ riscv_out_t riscv_name(
111124
#pragma FEEDBACK reg_wr_addr
112125
#pragma FEEDBACK reg_wr_data
113126
#pragma FEEDBACK reg_wr_en
114-
if(stage_valid[1]|stage_valid[2]){
127+
if(state[1]|state[2]){
115128
reg_file_out = reg_file(
116129
decoded.src1, // First read port address
117130
decoded.src2, // Second read port address
@@ -120,31 +133,31 @@ riscv_out_t riscv_name(
120133
reg_wr_en // Write enable
121134
);
122135
// Next state/cycle (potentially redundant)
123-
next_stage_valid = stage_valid;
124-
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
136+
next_state = state;
137+
ARRAY_1ROT_UP(uint1_t, next_state, N_CYCLES)
125138
}
126139

127140
// Cycle1: Execute
128-
if(stage_valid[1]){
141+
if(state[1]){
129142
printf("Execute in Cycle/Stage 1\n");
130143
exe = execute(
131-
pc, pc_plus4,
144+
pc, pc_plus4_reg,
132145
decoded,
133146
reg_file_out.rd_data1, reg_file_out.rd_data2
134147
);
135148
// Next state/cycle (potentially redundant)
136-
next_stage_valid = stage_valid;
137-
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
149+
next_state = state;
150+
ARRAY_1ROT_UP(uint1_t, next_state, N_CYCLES)
138151
}
139152

140153
// Boundary shared between cycle1 and cycle2
141154
// Data Memory inputs in stage1
142155
// Default no writes or reads
143156
ARRAY_SET(mem_wr_byte_ens, 0, 4)
144157
ARRAY_SET(mem_rd_byte_ens, 0, 4)
145-
uint32_t mem_addr = exe.result; // addr always from execute module, not always used
146-
uint32_t mem_wr_data = reg_file_out.rd_data2;
147-
if(stage_valid[1]){
158+
mem_addr = exe.result; // addr always from execute module, not always used
159+
mem_wr_data = reg_file_out.rd_data2;
160+
if(state[1]){
148161
// Only write or read en during first cycle of two cycle read
149162
mem_wr_byte_ens = decoded.mem_wr_byte_ens;
150163
mem_rd_byte_ens = decoded.mem_rd_byte_ens;
@@ -170,10 +183,10 @@ riscv_out_t riscv_name(
170183
o.mem_map_outputs = dmem_out.mem_map_outputs;
171184
#endif
172185
// Data memory outputs in stage2
173-
if(stage_valid[2]){
186+
if(state[2]){
174187
// Read output available from dmem_out in second cycle of two cycle read
175-
if(decoded.mem_rd_byte_ens[0]){
176-
printf("Read Mem[0x%X] = %d\n", mem_addr, dmem_out.rd_data);
188+
if(decoded_reg.mem_rd_byte_ens[0]){
189+
printf("Read Mem[0x%X] = %d\n", mem_addr_reg, dmem_out.rd_data);
177190
}
178191
}
179192

@@ -182,27 +195,37 @@ riscv_out_t riscv_name(
182195
reg_wr_en = 0; // default no writes
183196
reg_wr_addr = 0;
184197
reg_wr_data = 0;
185-
if(stage_valid[2]){
198+
if(state[2]){
186199
printf("Write Back + Next PC in Cycle/Stage 2\n");
187200
// Reg file write back, drive inputs (FEEDBACK)
188-
reg_wr_en = decoded.reg_wr;
189-
reg_wr_addr = decoded.dest;
201+
reg_wr_en = decoded_reg.reg_wr;
202+
reg_wr_addr = decoded_reg.dest;
190203
// Determine reg data to write back (sign extend etc)
191204
reg_wr_data = select_reg_wr_data(
192-
decoded,
193-
exe,
205+
decoded_reg,
206+
exe_reg,
194207
dmem_out.rd_data,
195-
pc_plus4
208+
pc_plus4_reg
196209
);
197210
// Branch/Increment PC
198-
pc = select_next_pc(decoded, exe, pc_plus4);
211+
pc = select_next_pc(decoded_reg, exe_reg, pc_plus4_reg);
199212
// Next state/cycle (potentially redundant)
200-
next_stage_valid = stage_valid;
201-
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
213+
next_state = state;
214+
ARRAY_1ROT_UP(uint1_t, next_state, N_CYCLES)
202215
}
203216

204217
// Reg update
205-
stage_valid = next_stage_valid;
218+
state = next_state;
219+
pc_plus4_reg = pc_plus4;
220+
imem_out_reg = imem_out;
221+
decoded_reg = decoded;
222+
reg_file_out_reg = reg_file_out;
223+
exe_reg = exe;
224+
mem_wr_byte_ens_reg = mem_wr_byte_ens;
225+
mem_rd_byte_ens_reg = mem_rd_byte_ens;
226+
mem_addr_reg = mem_addr;
227+
mem_wr_data_reg = mem_wr_data;
228+
dmem_out_reg = dmem_out;
206229

207230
return o;
208231
}

0 commit comments

Comments
 (0)