Skip to content

Commit 073cd7e

Browse files
First version of multi cycle riscv seems working
1 parent 4ca9aef commit 073cd7e

File tree

3 files changed

+317
-0
lines changed

3 files changed

+317
-0
lines changed

examples/risc-v/mem_decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ begin \n\
5151
#ifdef RISCV_IMEM_1_CYCLE
5252
// One cycle reads version
5353
#define riscv_imem_ram PPCAT(riscv_name,_imem_ram_one_cycle)
54+
#ifndef RISCV_IMEM_NO_AUTOPIPELINE
5455
PRAGMA_MESSAGE(FUNC_LATENCY riscv_imem_ram 1)
56+
#endif
5557
riscv_imem_ram_out_t riscv_imem_ram(
5658
uint32_t addr1,
5759
uint1_t valid1
@@ -154,7 +156,9 @@ begin \n\
154156
#ifdef RISCV_DMEM_1_CYCLE
155157
// One cycle reads version
156158
#define riscv_dmem_ram PPCAT(riscv_name,_dmem_ram_one_cycle)
159+
#ifndef RISCV_DMEM_NO_AUTOPIPELINE
157160
PRAGMA_MESSAGE(FUNC_LATENCY riscv_dmem_ram 1)
161+
#endif
158162
riscv_dmem_ram_out_t riscv_dmem_ram(
159163
uint32_t addr0,
160164
uint32_t wr_data0, uint1_t wr_byte_ens0[4],
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#pragma PART "xc7a35ticsg324-1l"
2+
#include "uintN_t.h"
3+
#include "intN_t.h"
4+
5+
// RISC-V components
6+
#include "risc-v.h"
7+
8+
// Include test gcc compiled program
9+
#include "gcc_test/text_mem_init.h"
10+
#include "gcc_test/data_mem_init.h"
11+
12+
// Declare memory map information
13+
// Starts with shared with software memory map info
14+
#include "gcc_test/mem_map.h"
15+
// Define inputs and outputs
16+
typedef struct my_mmio_in_t{
17+
uint1_t button;
18+
}my_mmio_in_t;
19+
typedef struct my_mmio_out_t{
20+
uint32_t return_value;
21+
uint1_t halt;
22+
uint1_t led;
23+
}my_mmio_out_t;
24+
// Define the hardware memory for those IO
25+
RISCV_DECL_MEM_MAP_MOD_OUT_T(my_mmio_out_t)
26+
riscv_mem_map_mod_out_t(my_mmio_out_t) my_mem_map_module(
27+
RISCV_MEM_MAP_MOD_INPUTS(my_mmio_in_t)
28+
){
29+
// Outputs
30+
static riscv_mem_map_mod_out_t(my_mmio_out_t) o;
31+
o.addr_is_mapped = 0; // since o is static regs
32+
// Memory muxing/select logic
33+
// 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)
37+
return o;
38+
}
39+
40+
// Declare a RISCV core type using memory info
41+
#define riscv_name my_riscv
42+
#define RISCV_IMEM_INIT text_MEM_INIT // from gcc_test/
43+
#define RISCV_IMEM_SIZE_BYTES IMEM_SIZE // from gcc_test/
44+
#define RISCV_DMEM_INIT data_MEM_INIT // from gcc_test/
45+
#define RISCV_DMEM_SIZE_BYTES DMEM_SIZE // from gcc_test/
46+
#define riscv_mem_map my_mem_map_module
47+
#define riscv_mem_map_inputs_t my_mmio_in_t
48+
#define riscv_mem_map_outputs_t my_mmio_out_t
49+
#include "multi_cycle_risc-v_decl.h"
50+
51+
// Set clock of instances of CPU
52+
#define CPU_CLK_MHZ 60.0
53+
MAIN_MHZ(my_top, CPU_CLK_MHZ)
54+
55+
// LEDs for demo
56+
#include "leds/leds_port.c"
57+
58+
// Debug output ports for sim and hardware
59+
#include "debug_port.h"
60+
DEBUG_OUTPUT_DECL(uint1_t, unknown_op) // Unknown instruction
61+
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()
64+
65+
void my_top()
66+
{
67+
// Instance of core
68+
my_mmio_in_t in; // Disconnected for now
69+
my_riscv_out_t out = my_riscv(in);
70+
71+
// Sim debug
72+
unknown_op = out.unknown_op;
73+
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;
76+
77+
// Output LEDs for hardware debug
78+
leds = 0;
79+
leds |= (uint4_t)out.mem_map_outputs.led << 0;
80+
leds |= (uint4_t)mem_out_of_range << 1;
81+
leds |= (uint4_t)unknown_op << 2;
82+
}
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// TODO rename defined things to be MULTI_CYCLE_RISCV_... etc
2+
#include "uintN_t.h"
3+
#include "intN_t.h"
4+
5+
// Support old single core using mem_map_out_t
6+
#include "mem_map.h"
7+
#ifdef riscv_mem_map_outputs_t
8+
#define riscv_mmio_mod_out_t riscv_mem_map_mod_out_t(riscv_mem_map_outputs_t)
9+
#else
10+
#define riscv_mmio_mod_out_t mem_map_out_t
11+
#endif
12+
13+
// Declare instruction and data memory
14+
// also includes memory mapped IO
15+
#define RISCV_IMEM_1_CYCLE
16+
#define RISCV_DMEM_1_CYCLE
17+
// Multi cycle is not a pipeline
18+
#define RISCV_IMEM_NO_AUTOPIPELINE
19+
#define RISCV_DMEM_NO_AUTOPIPELINE
20+
#define N_CYCLES 3
21+
#include "mem_decl.h"
22+
23+
// CPU top level
24+
#define riscv_out_t PPCAT(riscv_name,_out_t)
25+
typedef struct riscv_out_t{
26+
// Debug IO
27+
uint1_t halt;
28+
uint32_t return_value;
29+
uint32_t pc;
30+
uint1_t unknown_op;
31+
uint1_t mem_out_of_range;
32+
#ifdef riscv_mem_map_outputs_t
33+
riscv_mem_map_outputs_t mem_map_outputs;
34+
#endif
35+
}riscv_out_t;
36+
riscv_out_t riscv_name(
37+
// Optional inputs to memory map
38+
#ifdef riscv_mem_map_inputs_t
39+
riscv_mem_map_inputs_t mem_map_inputs
40+
#endif
41+
)
42+
{
43+
// Top level outputs
44+
riscv_out_t o;
45+
46+
// Instead of a counter for which stage is active
47+
// 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
50+
51+
// CPU registers (outside of reg file)
52+
static uint32_t pc = 0;
53+
o.pc = pc; // debug
54+
// Wires/non-static local variables from original single cycle design
55+
// are likely to be shared/stored from cycle to cycle now
56+
// 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;
67+
68+
// Cycle 0: PC
69+
if(stage_valid[0]){
70+
printf("PC in Cycle/Stage 0\n");
71+
// Program counter is input to IMEM
72+
pc_plus4 = pc + 4;
73+
printf("PC = 0x%X\n", pc);
74+
// Next state/cycle (potentially redundant)
75+
next_stage_valid = stage_valid;
76+
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
77+
}
78+
79+
// Boundary shared between cycle0 and cycle1
80+
if(stage_valid[0]|stage_valid[1]){
81+
// Instruction memory
82+
imem_out = riscv_imem_ram(pc>>2, 1);
83+
}
84+
85+
// Cycle1: Decode
86+
if(stage_valid[1]){
87+
printf("Decode in Cycle/Stage 1\n");
88+
// Decode the instruction to control signals
89+
printf("Instruction: 0x%X\n", imem_out.rd_data1);
90+
decoded = decode(imem_out.rd_data1);
91+
if(decoded.print_rs1_read){
92+
printf("Read RegFile[%d] = %d\n", decoded.src1, reg_file_out.rd_data1);
93+
}
94+
if(decoded.print_rs2_read){
95+
printf("Read RegFile[%d] = %d\n", decoded.src2, reg_file_out.rd_data2);
96+
}
97+
o.unknown_op = decoded.unknown_op; // debug
98+
// Next state/cycle (potentially redundant)
99+
next_stage_valid = stage_valid;
100+
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
101+
}
102+
103+
// Cycle1+2: Register file reads and writes
104+
// Reads are done during cycle1
105+
// Write back is next clock, cycle2
106+
// Register file write signals are not driven until later in code
107+
// but are used now, requiring FEEDBACK pragma
108+
uint5_t reg_wr_addr;
109+
uint32_t reg_wr_data;
110+
uint1_t reg_wr_en;
111+
#pragma FEEDBACK reg_wr_addr
112+
#pragma FEEDBACK reg_wr_data
113+
#pragma FEEDBACK reg_wr_en
114+
if(stage_valid[1]|stage_valid[2]){
115+
reg_file_out = reg_file(
116+
decoded.src1, // First read port address
117+
decoded.src2, // Second read port address
118+
reg_wr_addr, // Write port address
119+
reg_wr_data, // Write port data
120+
reg_wr_en // Write enable
121+
);
122+
// Next state/cycle (potentially redundant)
123+
next_stage_valid = stage_valid;
124+
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
125+
}
126+
127+
// Cycle1: Execute
128+
if(stage_valid[1]){
129+
printf("Execute in Cycle/Stage 1\n");
130+
exe = execute(
131+
pc, pc_plus4,
132+
decoded,
133+
reg_file_out.rd_data1, reg_file_out.rd_data2
134+
);
135+
// Next state/cycle (potentially redundant)
136+
next_stage_valid = stage_valid;
137+
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
138+
}
139+
140+
// Boundary shared between cycle1 and cycle2
141+
// Data Memory inputs in stage1
142+
// Default no writes or reads
143+
ARRAY_SET(mem_wr_byte_ens, 0, 4)
144+
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]){
148+
// Only write or read en during first cycle of two cycle read
149+
mem_wr_byte_ens = decoded.mem_wr_byte_ens;
150+
mem_rd_byte_ens = decoded.mem_rd_byte_ens;
151+
if(decoded.mem_wr_byte_ens[0]){
152+
printf("Write Mem[0x%X] = %d\n", mem_addr, mem_wr_data);
153+
}
154+
}
155+
// DMEM always "in use" regardless of stage
156+
// since memory map IO need to be connected always
157+
dmem_out = riscv_dmem(
158+
mem_addr, // Main memory read/write address
159+
mem_wr_data, // Main memory write data
160+
mem_wr_byte_ens, // Main memory write data byte enables
161+
mem_rd_byte_ens // Main memory read enable
162+
// Optional memory map inputs
163+
#ifdef riscv_mem_map_inputs_t
164+
, mem_map_inputs
165+
#endif
166+
);
167+
o.mem_out_of_range = dmem_out.mem_out_of_range; // debug
168+
// Optional outputs from memory map
169+
#ifdef riscv_mem_map_outputs_t
170+
o.mem_map_outputs = dmem_out.mem_map_outputs;
171+
#endif
172+
// Data memory outputs in stage2
173+
if(stage_valid[2]){
174+
// 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);
177+
}
178+
}
179+
180+
// Cycle 2: Write Back + Next PC
181+
// default values needed for feedback signals
182+
reg_wr_en = 0; // default no writes
183+
reg_wr_addr = 0;
184+
reg_wr_data = 0;
185+
if(stage_valid[2]){
186+
printf("Write Back + Next PC in Cycle/Stage 2\n");
187+
// Reg file write back, drive inputs (FEEDBACK)
188+
reg_wr_en = decoded.reg_wr;
189+
reg_wr_addr = decoded.dest;
190+
// Determine reg data to write back (sign extend etc)
191+
reg_wr_data = select_reg_wr_data(
192+
decoded,
193+
exe,
194+
dmem_out.rd_data,
195+
pc_plus4
196+
);
197+
// Branch/Increment PC
198+
pc = select_next_pc(decoded, exe, pc_plus4);
199+
// Next state/cycle (potentially redundant)
200+
next_stage_valid = stage_valid;
201+
ARRAY_1ROT_UP(uint1_t, next_stage_valid, N_CYCLES)
202+
}
203+
204+
// Reg update
205+
stage_valid = next_stage_valid;
206+
207+
return o;
208+
}
209+
210+
211+
#undef riscv_name
212+
#undef RISCV_IMEM_SIZE_BYTES
213+
#undef RISCV_DMEM_SIZE_BYTES
214+
#undef RISCV_IMEM_NUM_WORDS
215+
#undef RISCV_DMEM_NUM_WORDS
216+
#undef RISCV_IMEM_INIT
217+
#undef RISCV_DMEM_INIT
218+
#undef riscv_mem_map
219+
#undef riscv_mmio_mod_out_t
220+
#undef riscv_mem_map_inputs_t
221+
#undef riscv_mem_map_outputs_t
222+
#undef riscv_imem_ram_out_t
223+
#undef riscv_dmem_ram_out_t
224+
#undef riscv_imem_ram
225+
#undef riscv_dmem_ram
226+
#undef riscv_imem_out_t
227+
#undef riscv_dmem_out_t
228+
#undef riscv_imem
229+
#undef riscv_dmem
230+
#undef RISCV_IMEM_NO_AUTOPIPELINE
231+
#undef RISCV_DMEM_NO_AUTOPIPELINE

0 commit comments

Comments
 (0)