src added not working or anything
This commit is contained in:
parent
8b05b7b07b
commit
e1227820dd
|
@ -0,0 +1 @@
|
||||||
|
work/
|
19
README.md
19
README.md
|
@ -1,3 +1,18 @@
|
||||||
# TIS-100-FPGA
|
# TIS-100 in Lucid for FPGA
|
||||||
|
|
||||||
A try to implement TIS 100 on a FPGA
|
I tried to implement [TIS-100](http://www.zachtronics.com/tis-100/) on a FPGA. I used the [Mojo-v3](https://alchitry.com/products/mojo-v3) from alchitry.
|
||||||
|
|
||||||
|
You need a tool (i am going to create one soon) to send a savefile from you computer to the programed FPGA.
|
||||||
|
|
||||||
|
Things currently not supported:
|
||||||
|
* ANY register - would just act like the NIL register
|
||||||
|
* graphical Output - nowhere to output too
|
||||||
|
* Stackstorage Nodes - comimg later
|
||||||
|
* debugging in any way
|
||||||
|
* speed control (step, fast run)
|
||||||
|
|
||||||
|
Keep in mind, that this is just a small Project to play around with a FPGA. I am pretty new to Lucid (the Language used) and i probably made every mistake - and bug - possible. So there is no warranty at all! But if you find something feel free to send me a message at `y.g.2 (at) gmx (dot) de`
|
||||||
|
|
||||||
|
###### Licence
|
||||||
|
|
||||||
|
This Project is Licenced under AGPLv3 a copy of the Licence should be alongside with the code.
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
module mojo_top (
|
||||||
|
input clk, // 50MHz clock
|
||||||
|
input rst_n, // reset button (active low)
|
||||||
|
output led [8], // 8 user controllable LEDs
|
||||||
|
input cclk, // configuration clock, AVR ready when high
|
||||||
|
output spi_miso, // AVR SPI MISO
|
||||||
|
input spi_ss, // AVR SPI Slave Select
|
||||||
|
input spi_mosi, // AVR SPI MOSI
|
||||||
|
input spi_sck, // AVR SPI Clock
|
||||||
|
output spi_channel [4], // AVR general purpose pins (used by default to select ADC channel)
|
||||||
|
input avr_tx, // AVR TX (FPGA RX)
|
||||||
|
output avr_rx, // AVR RX (FPGA TX)
|
||||||
|
input avr_rx_busy // AVR RX buffer full
|
||||||
|
) {
|
||||||
|
|
||||||
|
sig rst; // reset signal
|
||||||
|
|
||||||
|
.clk(clk) {
|
||||||
|
// The reset conditioner is used to synchronize the reset signal to the FPGA
|
||||||
|
// clock. This ensures the entire FPGA comes out of reset at the same time.
|
||||||
|
reset_conditioner reset_cond;
|
||||||
|
}
|
||||||
|
|
||||||
|
always {
|
||||||
|
reset_cond.in = ~rst_n; // input raw inverted reset signal
|
||||||
|
rst = reset_cond.out; // conditioned reset
|
||||||
|
|
||||||
|
led = 8h00; // turn LEDs off
|
||||||
|
spi_miso = bz; // not using SPI
|
||||||
|
spi_channel = bzzzz; // not using flags
|
||||||
|
avr_rx = bz; // not using serial port
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
|
||||||
|
global GNode {
|
||||||
|
const ROWS = 14; //number of commands to hold
|
||||||
|
const CMDLEN = 16; //number of bits a command is in size
|
||||||
|
const WORDLEN = 11; //bits to store a number (-999 to 999)
|
||||||
|
const REGISTERADDRLEN = 3; //bits to address a register
|
||||||
|
|
||||||
|
struct cmd {
|
||||||
|
cmd[4],
|
||||||
|
arg_is_const, //1 = argument is a 11bit signed integer, 0 argument is a register number
|
||||||
|
args[11] //11 bit number or at most 2 reigster (mov cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Register numbers:
|
||||||
|
0 = NIL
|
||||||
|
1 = ACC
|
||||||
|
2 = UP
|
||||||
|
3 = RIGHT
|
||||||
|
4 = DOWN
|
||||||
|
5 = LEFT
|
||||||
|
6 = LAST
|
||||||
|
7 = ANY
|
||||||
|
|
||||||
|
Subclk:
|
||||||
|
0 = Read cmd
|
||||||
|
1 = interpret cmd
|
||||||
|
2 = Mark input as read
|
||||||
|
3 = try to read
|
||||||
|
4 = mark output as read
|
||||||
|
5 = write to output
|
||||||
|
6 = increment pc and reset everything thats not needed anymore
|
||||||
|
|
||||||
|
cycles are skiped, if node is waiting for another node to read or write a connection
|
||||||
|
*/
|
||||||
|
|
||||||
|
module node (
|
||||||
|
input clk, // clock
|
||||||
|
input rst, // reset
|
||||||
|
|
||||||
|
input up_in[GNode.WORDLEN],
|
||||||
|
output<G_nodeCon.nodeConIn> up_out,
|
||||||
|
|
||||||
|
input right_in[GNode.WORDLEN],
|
||||||
|
output<G_nodeCon.nodeConIn> right_out,
|
||||||
|
|
||||||
|
input down_in[GNode.WORDLEN],
|
||||||
|
output<G_nodeCon.nodeConIn> down_out,
|
||||||
|
|
||||||
|
input left_in[GNode.WORDLEN],
|
||||||
|
output<G_nodeCon.nodeConIn> left_out,
|
||||||
|
|
||||||
|
output error,
|
||||||
|
input subclk
|
||||||
|
) {
|
||||||
|
|
||||||
|
.clk(clk), .rst(rst) {
|
||||||
|
dff pc[4](#INIT(0)); //program counter
|
||||||
|
|
||||||
|
dff<GNode.cmd> cmds[GNode.ROWS];
|
||||||
|
|
||||||
|
dff lastreg[3](#INIT(0)); // default = NIL
|
||||||
|
|
||||||
|
dff acc[GNode.WORDLEN](#INIT(0));
|
||||||
|
dff bak[GNode.WORDLEN](#INIT(0));
|
||||||
|
|
||||||
|
dff is_reading(#INIT(0));
|
||||||
|
dff is_writing(#INIT(0));
|
||||||
|
|
||||||
|
dff<GNode.cmd> currentCmd;
|
||||||
|
fsm currentCmdFSM = {MOV, // 2 args: 1const, 1reg or 2 reg
|
||||||
|
JEZ, JNZ, JGZ, JLZ, JRO, // 1 arg: 5bit(signed) const offset
|
||||||
|
ADD, SUB, // 1 arg: 1reg or 1 signed const
|
||||||
|
NEG, SWP, SAV}; // no arg
|
||||||
|
dff currentCmd_has_const(#INIT(0));
|
||||||
|
dff currentCmd_const[GNode.WORDLEN](#INIT(0));
|
||||||
|
dff currentCmd_has_reg[2](#INIT(0)); // only mov has two arguments (dst ist first; src ist second)
|
||||||
|
dff currentCmd_reg[2][3];//registers to use TODO: add #INIT(0)
|
||||||
|
|
||||||
|
registerMultiplex reg1(.selection(currentCmd_reg[0]), .acc(acc), .up(up), .right(right), .down(down), .left(left), .last(lastreg));
|
||||||
|
}
|
||||||
|
|
||||||
|
always {
|
||||||
|
error = 0;
|
||||||
|
if(subclk == 0 && is_reading.q == 0 && is_writing.q == 0) {
|
||||||
|
//read command
|
||||||
|
currentCmd.d = cmds.q[pc.q];
|
||||||
|
currentCmdFSM.d = cmds.q[pc.q];
|
||||||
|
} else if(subclk == 1 && is_reading.q == 0 && is_writing.q == 0) {
|
||||||
|
//interpret
|
||||||
|
|
||||||
|
//set them for all commands just set the _has_ flag if reguired
|
||||||
|
currentCmd_reg.d[0] = currentCmd.q.args[ GNode.REGISTERADDRLEN -1 : 0]; //3 LSB bytes = register 1
|
||||||
|
currentCmd_reg.d[1] = currentCmd.q.args[(2*GNode.REGISTERADDRLEN) -1 : GNode.REGISTERADDRLEN]; //3 next bytes = register 2
|
||||||
|
currentCmd_const.d = currentCmd.q.args;
|
||||||
|
|
||||||
|
//set the _has_ flags
|
||||||
|
currentCmd_has_const.d = (currentCmdFSM.q >= currentCmdFSM.JEZ && currentCmdFSM.q <= currentCmdFSM.JLZ) || currentCmd.q.arg_is_const == 1; //last bit of cmd indicates a constant for cmd's that can do both
|
||||||
|
currentCmd_has_reg.d[0] = (currentCmdFSM.q == currentCmdFSM.MOV || currentCmd.q.arg_is_const == 0); //mov cmd -> at least 1 reg or no constant
|
||||||
|
currentCmd_has_reg.d[1] = (currentCmdFSM.q == currentCmdFSM.MOV && currentCmd.q.arg_is_const == 0); //mov cmd und arg ist keine constante -> 2 register
|
||||||
|
} else if(subclk == 2 && is_reading.q == 0 && is_writing.q == 0) {
|
||||||
|
//mark input as read
|
||||||
|
is_reading.d = 1;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
|
||||||
|
A Connection between two Nodes, blocks
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
global G_nodeCon {
|
||||||
|
const WIDTH = 10;
|
||||||
|
|
||||||
|
struct nodeConIn {
|
||||||
|
write,
|
||||||
|
read,
|
||||||
|
enable,
|
||||||
|
in[WIDTH]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module nodeCon (
|
||||||
|
|
||||||
|
input clk, // clock
|
||||||
|
input rst, // reset
|
||||||
|
|
||||||
|
input<G_nodeCon.nodeConIn> a_in,
|
||||||
|
output a_out[G_nodeCon.WIDTH],
|
||||||
|
|
||||||
|
output done, //erledigt (blokirung kann uafgehoben werden)
|
||||||
|
|
||||||
|
/*input b_write,
|
||||||
|
input b_read,
|
||||||
|
input b_enable,
|
||||||
|
input b_in[WIDTH],*/
|
||||||
|
input<G_nodeCon.nodeConIn> b_in,
|
||||||
|
output b_out[G_nodeCon.WIDTH],
|
||||||
|
|
||||||
|
output error
|
||||||
|
) {
|
||||||
|
|
||||||
|
dff internalBuff[G_nodeCon.WIDTH](#INIT(0), .clk(clk), .rst(rst));
|
||||||
|
|
||||||
|
always {
|
||||||
|
error = ((a_in.write && b_in.write) || (a_in.read && b_in.read)) && ~rst && a_in.enable && b_in.enable; // gleichzeitiges lesen oder schreiben und nicht reset
|
||||||
|
|
||||||
|
done = (a_in.write && b_in.write) || (a_in.read && b_in.write) && a_in.enable && b_in.enable;
|
||||||
|
|
||||||
|
a_out = 0;
|
||||||
|
if(a_in.enable) {
|
||||||
|
if(a_in.write) {
|
||||||
|
internalBuff.d = a_in;
|
||||||
|
} else if(a_in.read) {
|
||||||
|
a_out = internalBuff.q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b_out = 0;
|
||||||
|
if(b_in.enable) {
|
||||||
|
if(b_in.write) {
|
||||||
|
internalBuff.d = b_in;
|
||||||
|
} else if(b_in.read) {
|
||||||
|
b_out = internalBuff.q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
module registerMultiplex (
|
||||||
|
input clk, // clock
|
||||||
|
input rst, // reset
|
||||||
|
|
||||||
|
|
||||||
|
input selection[3], //which register? see node.luc for enumeration
|
||||||
|
|
||||||
|
input acc[GNode.WORDLEN],
|
||||||
|
input up[GNode.WORDLEN],
|
||||||
|
input right[GNode.WORDLEN],
|
||||||
|
input down[GNode.WORDLEN],
|
||||||
|
input left[GNode.WORDLEN],
|
||||||
|
input last[GNode.REGISTERADDRLEN], //CAREFULL! this is NOT a register! this is a address!
|
||||||
|
input any[GNode.WORDLEN],
|
||||||
|
|
||||||
|
output out[GNode.WORDLEN],
|
||||||
|
input in[GNode.WORDLEN],
|
||||||
|
input write,
|
||||||
|
input read
|
||||||
|
|
||||||
|
) {
|
||||||
|
|
||||||
|
dff outb[GNode.WORDLEN](.clk(clk), .rst(rst), #INIT(0));
|
||||||
|
|
||||||
|
always {
|
||||||
|
//reading
|
||||||
|
if(read) {
|
||||||
|
if(selection == 0) {
|
||||||
|
outb.d = 0;
|
||||||
|
} else if(selection == 1) {
|
||||||
|
outb.d = acc;
|
||||||
|
} else if(selection == 2) {
|
||||||
|
outb.d = up;
|
||||||
|
} else if(selection == 3) {
|
||||||
|
outb.d = right;
|
||||||
|
} else if(selection == 4) {
|
||||||
|
outb.d = down;
|
||||||
|
} else if(selection == 5) {
|
||||||
|
outb.d = left;
|
||||||
|
} else if(selection == 6) {
|
||||||
|
if(last == 0) { //inenr selection for last
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if(selection == 7) {
|
||||||
|
outb.d = any;
|
||||||
|
} else {
|
||||||
|
outb.d = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outb.d = 0;
|
||||||
|
}
|
||||||
|
out = outb.q;
|
||||||
|
|
||||||
|
if(write) {
|
||||||
|
//TODO: fwd in to the register and set writing flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
module tis100 (
|
||||||
|
input clk, // clock
|
||||||
|
input rst, // reset
|
||||||
|
output out
|
||||||
|
) {
|
||||||
|
|
||||||
|
always {
|
||||||
|
out = 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="tis-100" board="Mojo" language="Lucid" version="2">
|
||||||
|
<files>
|
||||||
|
<src>node.luc</src>
|
||||||
|
<src>tis100.luc</src>
|
||||||
|
<src top="true">mojo_top.luc</src>
|
||||||
|
<src>nodeCon.luc</src>
|
||||||
|
<src>registerMultiplex.luc</src>
|
||||||
|
<component>reset_conditioner.luc</component>
|
||||||
|
<constraint lib="true">mojo.ucf</constraint>
|
||||||
|
</files>
|
||||||
|
</project>
|
Loading…
Reference in New Issue