This project is maintained by lawrie
Prev | Up | Next |
A soft processor is an FPGA implementation of a CPU, or more accurately of an Instruction Set Architecture (ISA).
A variety of ISAs have been implemented on BlackIce and Blackice Mx including OPC, Z80, 6502 and RISC-V.
The Z80 and 6502 implementations are described in the Retro Computing chapter below.
There is a family of soft processors known as Open Page Code (OPC). They implement a family of simple ISAs that can be implemented on one page of computer printout paper. The OPC6 variant is available on BlackIce but has not yet been ported to BlackIce Mx.
There is a professional open source ISA called Risc-V. It is well supported by the GNU tool chain and can be considered as an open source alternative to the ARM family of ISAs.
This book will concentrate on the picorv32 implementation of Risc-V, written by Clifford Wolf and VexRiscv, written by Charles Papon.
There are other RISC-V implementations that have been ported to BlackIce II, including icicle. Icicle uses the full 64-bit Risc-V toolchain. The newlib version is recommended.
One of the simplest instructions set ever used for a computer, was the instruction set of the Manchester Baby computer. It had just six instructions:
Code | Mnemonic | Description |
---|---|---|
000 | JMP | Unconditional jump to absolute address |
001 | JRP | Relative unconditional jump |
002 | LDN | Load negative |
003 | STO | Store |
004 005 | SUB | Subtract |
006 | CMP | Compare – skip instruction if accumulator is negative |
007 | STP | Halts execution |
There were just 32 words of 32-bit data in the first implementation of the BABy, otherwise known as the Small Scale Experimental Machine.
Here is a BlackIce Mx implementation of the Baby:
baby.v:
module baby(
input clk25,
input button,
output [2:0] led,
output [7:0] led2
);
reg [31:0] a, d;
reg [4:0] ci, addr;
reg [2:0] f;
reg [2:0] state = WAITING;
reg we, re;
wire clk = clk25;
localparam WAITING = 0, SCAN1 = 1, ACTION1 = 2,
SCAN2 = 3, SCAN3 = 4, ACTION2 = 5,
ACTION3 = 6, STOPPED = 7;
line_ram ram (.clk(clk), .addr(addr), .din(a),
.we(we), .re(re), .dout(d));
always @(posedge clk)
begin
case (state)
WAITING: if (~button) state = SCAN1; // Start when button pressed
SCAN1: begin re <= 1; ci <= ci + 1; addr <= ci + 1; state <= ACTION1; end
ACTION1: begin; re <= 0; state <= SCAN2; end // delay for BRAM
SCAN2:
begin
addr <= d[31:27];
f <= d[18:16];
state <= ACTION2;
if (d[18:16] == 3) we <= 1;
else if (d[18:16] < 6) re <= 1;
end
ACTION2: begin re <= 0; we <= 0; state <= ACTION3; end
ACTION3:
begin
state <= SCAN1;
case (f)
0: ci <= d; // JMP
1: ci <= ci + d; // JRP
2: a <= -d; // LDN
4, 5: a <= a - d; // SUB
6: if ($signed(a) < 0) ci <= ci + 1; // CMP
7: state <= STOPPED; // STOP
endcase
end
endcase
end
assign led = ~{2'b00, state == STOPPED};
assign led2 = a; // Show accumular at end of program
endmodule
module line_ram(
input clk,
input [4:0] addr,
output reg [31:0] dout,
input [31:0] din,
input we,
input re
);
reg [31:0] ram [0:31];
initial $readmemh("lines.hex", ram);
always @(posedge clk)
begin
if (we) ram[addr] <= din;
else if (re) dout <= ram[addr];
end
endmodule
baby.pcf:
set_io clk25 60
set_io -pullup yes button 49
set_io led[0] 52
set_io led[1] 55
set_io led[2] 56
set_io led2[0] 34
set_io led2[1] 33
set_io led2[2] 29
set_io led2[3] 28
set_io led2[4] 38
set_io led2[5] 37
set_io led2[6] 32
set_io led2[7] 31
Makefile:
VERILOG_FILES = baby.v
PCF_FILE = baby.pcf
include ../blackicemx.mk
The 32x32bit RAM is implemented using a BRAM memory module. More information on this is in the Memory chapter.
The Baby ordered bits from least significant first. This implementation does not stick to that convention, but is otherwise a fairly accurate emulation of the Baby.
Here is an example program in the file, lines.hex :
00000000
e8020000
c8030000
c8020000
d0030000
d0020000
00060000
d8000000
f8020000
f0040000
c8030000
c8020000
f8030000
d0020000
e0040000
c8030000
c8020000
d0030000
c0000000
f8020000
c8030000
c8020000
00070000
00000000
00000004
00000000
00000000
00000012
ffffffff
00000005
00000032
00000000
And here is the disassembly of the example program.
0 JMP 0
1 LDN 29
2 STO 25
3 LDN 25
4 STO 26
5 LDN 26
6 CMP 0
7 JMP 27
8 LDN 31
9 SUB 30
10 STO 25
11 LDN 25
12 STO 31
13 LDN 26
14 SUB 28
15 STO 25
16 LDN 25
17 STO 26
18 JMP 24
19 LDN 31
20 STO 25
21 LDN 25
22 STP 0
23 00000000
24 00000004
25 00000000
26 00000000
27 00000012
28 ffffffff
29 00000005
30 00000032
31 00000000
This program multiplies the numbers in lines 29 and 30 leaving the result in line 31 and the accumulator.
Here is a video of the led panel version of this program:
A SoC is a soft processor, plus implementation of associated hardware, to produce a complete system on a chip or in our case on an FPGA chip. The hardware that is implemented as part of a SoC typically includes SPI and I2C implementations, GPIO access including LEDs, buttons, switches etc., a UART, PWM or I2S for audio output, and typically other input and output devices such as VGA monitors, LED panels, 7-segment displays, and PS/2 keyboards or mice. Access to such hardware modules is usually via a memory-mapped API.
A GNU (or other) toolchain is used to build an image file for the CPU to execute, and there needs to be some way that this can be loaded into memory. It can be copied into flash memory by the STM32 co-processor and then wither copied into RAM or executed in-place from the flash memory. Alternatively, it can be read from the UART, or from the SD card, or sent by via DSPI from the STM32.
Typically, some form of configuration is required to say which hardware modules a particular instance of the SoC requires and which pins they are connected to.
Clifford Wolf produced a SoC, or more accurately, a SoC genedrator, for the icoBoard, called icoSoC. This has been modified and renamed BlackSoC to run on the BlackIce II, but has not yet been ported to the BlackIce Mx. There is also a simpler picorv32 based SoC, written by Clifford Wolf, called picoSoC, which has a limited set of peripherals, but which can be extended.
In addition there is a Risc-V SoC called SaxonSoc, which uses the VexRiscv Risc-V implementation, and has been ported to Blackice Mx. Both VexRiscv and SaxonSoc are written in SpinalHDL and Charles Papon is the author of all three of these.
Prev | Up | Next |