I thought it might be nice to make some kind of simplistic 32-bit softcore CPU using an FPGA. The most natural opcodes (for me) would correspond to common C operations such arithmetic +, -, *, /, %, =, comparisons (and conditional jumps) <, >, <=, >=, !=, == and bit operations &, |, ^, ~, <<, >>. This is why I decided to call it CCPU. A simple assembler was written using C, a softcore CPU using verilog and a xorshift pseudorandom program using assembler.
--
0
MOV
A, 1 ;
this program will do xorshift32
2
MOV
B, 0 ;
displayed as noise on CCPUs VGA out
4
MOV
C, 1
6
MOV
D, 13
8
MOV
E, 17
10
MOV
F, 5
12
MOV
H, 256
14
MOV
I, 64000
16
SHL
G, A, D
17
XOR
A, A, G ;
A ^= A << 13
18
SHR
G, A, E
19
XOR
A, A, G ;
A ^= A >> 17
20
SHL
G, A, F
21
XOR
A, A, G ;
A ^= A << 5
22
ADD
B, B, C ;
B = B + 1
23
STO
B, A ;
MEM[B] = A
24
JL
B, I, 16 ;
if(B==64000) goto 16;
26
JMP
2 ;
goto 2;
--
module
xorshift(input
clock, output
VGA_HS, VGA_VS, VGA_B);
wire
cpu_clock;
wire
pixel_clock;
/*
VGA 640x400 70Hz */
parameter
H =
640; /*
width of visible area */
parameter
HFP =
16;
/*
unused time before hsync */
parameter
HS =
96; /* width of hsync
*/
parameter
HBP =
48;
/* unused time after hsync
*/
parameter
V =
400; /* height of visible area
*/
parameter
VFP =
12;
/* unused time before vsync
*/
parameter
VS =
2; /* width of vsync
*/
parameter
VBP =
35;
/* unused time after vsync
*/
reg
[9:0]
h_cnt; /* horizontal pixel counter
*/
reg
[9:0]
v_cnt; /* vertical pixel counter
*/
reg
hs, vs, pixel;
reg
vmem [320*200-1:0];
reg
[15:0]
video_counter;
/*
horizontal pixel counter */
always@(posedge
pclk)
begin
if(h_cnt
==
H+HFP+HS+HBP-1)
h_cnt <=
0;
else
h_cnt <=
h_cnt +
1;
if(h_cnt
==
H+HFP) hs <=
0;
if(h_cnt
==
H+HFP+HS) hs <=
1;
end
/*
vertical pixel counter */
always@(posedge
pclk)
begin
if(h_cnt
==
H+HFP)
begin
if(v_cnt
==
VS+VBP+V+VFP-1)
v_cnt <=
0;
else
v_cnt <=
v_cnt +
1;
if(v_cnt
==
V+VFP) vs <=
1;
if(v_cnt
==
V+VFP+VS) vs <=
0;
end
end
/*
RAMDAC */
always@(posedge
pclk)
begin
if((v_cnt
<
V) &&
(h_cnt <
H))
begin
if(h_cnt[0]
==
1)
video_counter
<=
video_counter +
1;
pixel
<=
vmem[video_counter];
end
else
begin
if(h_cnt
==
H+HFP)
begin
if(v_cnt
==
V+VFP)
video_counter
<=
0;
else
if((v_cnt
<
V) &&
(v_cnt[0] !=
1))
video_counter
<=
video_counter -
320;
end
pixel
<=
0;
end
end
assign
VGA_HS =
hs;
assign
VGA_VS =
vs;
assign
VGA_B =
pixel;
/*
VGA */
reg
[31:0]
mem[255:0];
reg
[31:0]
D;
reg
[31:0]
pc;
reg
[31:0]
cnt;
reg
[31:0]
r[31:0],
data;
reg
[7:0]
o, a, b, c;
reg
pclk, cclk;
initial
$readmemh("ram.hex",
mem);
initial
begin
pc
=
0;
o
=
0;
a
=
0;
b
=
0;
c
=
0;
end
always
@(posedge
clock)
begin
cnt
<=
cnt +
1;
pclk
<=
~pclk;
/* 25 MHz */
cclk
<=
~cclk;
end
always
@(posedge
cpu_clock)
begin
D
<=
mem[pc];
o
<=
D[31:24];
a
<=
D[23:16];
b
<=
D[15:8];
c
<=
D[7:0];
end
always
@(negedge
cpu_clock)
begin
data
<=
mem[pc+1];
case(o)
1:begin
/* JMP */
pc
<=
data;
end
6:begin
/* JL */
if(r[a]<r[b])
pc
<=
data;
else
pc
<=
pc +
1;
end
8:begin
/* MOV */
r[a]
<=
data;
pc
<=
pc +
2;
end
11:begin
/* STO */
vmem[r[a]]
<=
r[b];
pc
<=
pc +
1;
end
12:begin
/* SHL */
r[a]
<=
r[b] <<
r[c];
pc
<=
pc +
1;
end
13:begin
/* SHR */
r[a]
<=
r[b] >>
r[c];
pc
<=
pc +
1;
end
17:begin
/* XOR */
r[a]
<=
r[b] ^
r[c];
pc
<=
pc +
1;
end
18:begin
/* ADD */
r[a]
<=
r[b] +
r[c];
pc
<=
pc +
1;
end
endcase
end
assign
pixel_clock =
pclk;
assign
cpu_clock =
cclk;
endmodule
--
#include <stdio.h>
#include <stdlib.h>
/* Branching */
#define NOP 0
#define JMP 1
#define JE 2
#define JNE 3
#define JG 4
#define JGE 5
#define JL 6
#define JLE 7
/* Loading */
#define MOV 8
#define LDA 10
#define STO 11
/* Logic */
#define SHL 12
#define SHR 13
#define NOT 14
#define AND 15
#define OR 16
#define XOR 17
/* Arithmetic */
#define ADD 18
int isOpcode(char *opc, char *cmp) {
int i, j = 0;
for(i=0; i<3; i++)
if(opc[i] == cmp[i])
j++;
if(j==3)
return 1;
else
return 0;
}
int main() {
FILE *in = fopen("xorshift.asm", "r");
FILE *out = fopen("ram.bin", "wb");
FILE *outh = fopen("ram.hex", "w");
unsigned char buf[256], nbuf[64], op[3], regs[3];
int i, j, k, line = 0;
while(fgets(buf, 256, in)!=NULL) {
/* find opcode */
for(i=0; i<3; i++)
op[i] = buf[i];
for(i=0; i<64; i++)
nbuf[i] = 0;
i = 3;
j = 0;
while(buf[i]!='\n' && buf[i]!=';') {
if(buf[i]>47 && buf[i]<58)
nbuf[j++] = buf[i];
i++;
}
j = atoi(nbuf);
/* find registers */
i = 3;
k = 0;
while(buf[i]!='\n' && buf[i]!=';') {
if(buf[i]>64 && buf[i]<91)
regs[k++] = buf[i]-'A';
i++;
}
/* handle opcodes */
if(isOpcode(op, "NOP")) {
i = (NOP<<24);
fwrite(&i, 4, 1, out);
printf("%d\tNOP\n", line);
fprintf(outh, "%.8x\n", &i);
line += 1;
}
if(isOpcode(op, "JMP")) {
i = JMP<<24;
fwrite(&i, 4, 1, out);
fwrite(&j, 4, 1, out);
printf("%d\tJMP %d\n", line, j):
fprintf(outh, "%.8x\n", i);
fprintf(outh, "%.8x\n", j);
line += 2;
}
if(isOpcode(op, "JE ")) {
i = (JE<<24)+(regs[0]<<16)+(regs[1]<<8);
fwrite(&i, 4, 1, out);
fwrite(&j, 4, 1, out);
printf("%d\tJE %c, %c, %d\n", line, regs[0]+'A', regs[1]+'A', j);
fprintf(outh, "%.8x\n", i);
fprintf(outh, "%.8x\n", j);
line += 2;
}
if(isOpcode(op, "JNE")) {
i = (JNE<<24)+(regs[0]<<16)+(regs[1]<<8);
fwrite(&i, 4, 1, out);
fwrite(&j, 4, 1, out);
printf("%d\tJNE %c, %c, %d\n", line, regs[0]+'A', regs[1]+'A', j);
fprintf(outh, "%.8x\n", i);
fprintf(outh, "%.8x\n", j);
line += 2;
}
if(isOpcode(op, "JG ")) {
i = (JG<<24)+(regs[0]<<16)+(regs[1]<<8);
fwrite(&i, 4, 1, out);
fwrite(&j, 4, 1, out);
printf("%d\tJG %c, %c, %d\n", line, regs[0]+'A', regs[1]+'A', j);
fprintf(outh, "%.8x\n", i);
fprintf(outh, "%.8x\n", j);
line += 2;
}
if(isOpcode(op, "JGE")) {
i = (JGE<<24)+(regs[0]<<16)+(regs[1]<<8);
fwrite(&i, 4, 1, out);
fwrite(&j, 4, 1, out);
printf("%d\tJGE %c, %c, %d\n", line, regs[0]+'A', regs[1]+'A',j);
fprintf(outh, "%.8x\n", i);
fprintf(outh, "%.8x\n", j);
line += 2;
}
if(isOpcode(op, "JL ")) {
i = (JL<<24)+(regs[0]<<16)+(regs[1]<<8);
fwrite(&i, 4, 1, out);
fwrite(&j, 4, 1, out);
printf("%d\tJL %c, %c, %d\n", line, regs[0]+'A', regs[1]+'A', j);
fprintf(outh, "%.8x\n", i);
fprintf(outh, "%.8x\n", j);
line += 2;
}
if(isOpcode(op, "JLE")) {
i = (JLE<<24)+(regs[0]<<16)+(regs[1]<<8);
fwrite(&i, 4, 1, out);
fwrite(&j, 4, 1, out);
printf("%d\tJLE %c, %c, %d\n", line, regs[0]+'A', regs[1]+'A', j);
fprintf(outh, "%.8x\n", i);
fprintf(outh, "%.8x\n", j);
line += 2;
}
if(isOpcode(op, "MOV")) {
i = (MOV<<24)+(regs[0]<<16);
fwrite(&i, 4, 1, out);
fwrite(&j, 4, 1, out);
printf("%d\tMOV %c, %d\n", line, regs[0]+'A', j);
fprintf(outh, "%.8x\n", i);
fprintf(outh, "%.8x\n", j);
line += 2;
}
if(isOpcode(op, "LDA")) {
i = (LDA<<24)+(regs[0]<<16)+(regs[1]<<8);
fwrite(&i, 4, 1, out);
printf("%d\tLDA %c, %c\n", line, regs[0]+'A', regs[1]+'A');
fprintf(outh, "%.8x\n", i);
line += 1;
}
if(isOpcode(op, "STO")) {
i = (STO<<24)+(regs[0]<<16)+(regs[1]<<8);
fwrite(&i, 4, 1, out);
printf("%d\tSTO %c, %c\n", line, regs[0]+'A', regs[1]+'A');
fprintf(outh, "%.8x\n", i);
line += 1;
}
if(isOpcode(op, "SHL")) {
i = (SHL<<24)+(regs[0]<<16)+(regs[1]<<8)+regs[2];
fwrite(&i, 4, 1, out);
printf("%d\tSHL %c, %c, %c\n", line, regs[0]+'A', regs[1]+'A', regs[2]+'A');
fprintf(outh, "%.8x\n", i);
line += 1;
}
if(isOpcode(op, "SHR")) {
i = (SHR<<24)+(regs[0]<<16)+(regs[1]<<8)+regs[2];
fwrite(&i, 4, 1, out);
printf("%d\tSHR %c, %c, %c\n", line, regs[0]+'A', regs[1]+'A', regs[2]+'A');
fprintf(outh, "%.8x\n", i);
line += 1;
}
if(isOpcode(op, "NOT")) {
i = (NOT<<24)+(regs[0]<<16)+(regs[1]<<8);
fwrite(&i, 4, 1, out);
printf("%d\tNOT %c, %c\n", line, regs[0]+'A', regs[1]+'A');
fprintf(outh, "%.8x\n", i);
line += 1;
}
if(isOpcode(op, "AND")) {
i = (AND<<24)+(regs[0]<<16)+(regs[1]<<8)+regs[2];
fwrite(&i, 4, 1, out);
printf("%d\tAND %c, %c, %c\n", line, regs[0]+'A', regs[1]+'A', regs[2]+'A');
fprintf(outh, "%.8x\n", i);
line += 1;
}
if(isOpcode(op, "OR ")) {
i = (OR<<24)+(regs[0]<<16)+(regs[1]<<8)+regs[2];
fwrite(&i, 4, 1, out);
printf("%d\tOR %c, %c, %c\n", line, regs[0]+'A', regs[1]+'A', regs[2]+'A');
fprintf(outh, "%.8x\n", i);
line += 1;
}
if(isOpcode(op, "XOR")) {
i = (XOR<<24)+(regs[0]<<16)+(regs[1]<<8)+regs[2];
fwrite(&i, 4, 1, out);
printf("%d\tXOR %c, %c, %c\n", line, regs[0]+'A', regs[1]+'A', regs[2]+'A');
fprintf(outh, "%.8x\n", i);
line += 1;
}
if(isOpcode(op, "ADD")) {
i = (ADD<<24)+(regs[0]<<16)+(regs[1]<<8)+regs[2];
fwrite(&i, 4, 1, out);
printf("%d\tADD %c, %c, %c\n", line, regs[0]+'A', regs[1]+'A', regs[2]+'A');
fprintf(outh, "%.8x\n", i);
line += 1;
}
}
fflush(out);
fflush(outh);
fclose(in);
fclose(out);
fclose(outh);
}