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);
}


No comments:
Post a Comment