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