TIS-100-FPGA/source/node.luc

247 lines
8.1 KiB
Plaintext

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
const NULL = 11h0;
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 (not implemented correctly)
7 = ANY (not implemented correctly)
Subclk: (a counter that steps every clock cycle to execute one part of an command)
0 = Read cmd
1 = interpret cmd
2 = Mark input as read
3 = try to read
4 = calculate
5 = mark output as read
6 = write to output
7 = increment pc and reset everything thats not needed anymore and update last register special case: regiuster == 6!
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[3]
) {
sig to_reg[GNode.WORDLEN];
sig read_reg;
sig write_reg;
sig i[$clog2(GNode.ROWS)]; //DEBUG ONLY, counter variable for a for-loop
signed sig mainarg[GNode.WORDLEN]; //always reading
//sig argtwo[GNode.WORDLEN]; //always writing (mov cmd)
.clk(clk), .rst(rst) {
dff<GNode.cmd> cmds[GNode.ROWS];
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
#INIT(0) {
dff currentCmd_has_const;
signed dff currentCmd_const[GNode.WORDLEN];
dff currentCmd_has_reg[2]; // only mov has two arguments (dst ist first (every cmd); src ist second(only used for mov))
dff lastreg[3]; // which register to use with LAST keyword, default = NIL
signed dff acc[GNode.WORDLEN];
signed dff bak[GNode.WORDLEN];
dff is_reading; //is command reading from a register? reading from ACC or BAK may not count (depending on context)
dff is_writing; //is command writing to a register? writing to ACC or BAK may not count (should be a pseudonym for currentCmd == MOV)
dff is_reading_inProgress; //is reading or writing in progress? (block every thing then)
dff is_writing_inProgress; //only on of this two can be 1 !
dff pc[4]; //program counter (which instruction to execute?)
}
dff currentCmd_reg[2][3];//registers to use TODO: add #INIT(0)
//TODO: fix selection! - when select what as read or as write reg?
registerDemultiplex reg_read(.selection(currentCmd_reg.q[0]), .acc(acc.q), .up(up_in), .right(right_in), .down(down_in), .left(left_in), .last(lastreg.q), .any(11h0), .read(read_reg));
registerMultiplex reg_write(.selection(currentCmd_reg.q[1]), .lastadr(lastreg.q), .write(write_reg), .in(to_reg));
}
always {
//EXAMPLE CODE
//ADD 1
cmds.d[0].cmd = currentCmdFSM.ADD;
cmds.d[0].arg_is_const = 1;
cmds.d[0].args = 11d1; //1
//MOV ACC, LEFT
cmds.d[1].cmd = currentCmdFSM.MOV;
cmds.d[1].arg_is_const = 0;
cmds.d[1].args = 11b0101001; //0 5 1 (leer, left, acc) von acc -> left
//write NULL-OP's to the rest of memory
for(i = 2; i < GNode.ROWS; i++) {
cmds.d[1].cmd = currentCmdFSM.ADD;
cmds.d[1].arg_is_const = 1;
cmds.d[1].args = 11h0;
}
error = 0; // no error
read_reg = 0; //default to non reading
//define outputs
up_out.in = reg_write.up;
up_out.write = reg_write.up_w;
right_out.in = reg_write.right;
right_out.write = reg_write.right_w;
down_out.in = reg_write.down;
down_out.write = reg_write.down_w;
left_out.in = reg_write.left;
left_out.write = reg_write.left_w;
if(subclk == 0 && is_reading_inProgress.q == 0 && is_writing_inProgress.q == 0) {
//read command
currentCmd.d = cmds.q[pc.q];
currentCmdFSM.d = cmds.q[pc.q];
} else if(subclk == 1 && is_reading_inProgress.q == 0 && is_writing_inProgress.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
//set flags for reading and writing
is_reading.d = (currentCmdFSM.q == currentCmdFSM.MOV || ((currentCmdFSM.q == currentCmdFSM.ADD || currentCmdFSM.q == currentCmdFSM.SUB) && currentCmd_has_const.q == 0)); //mov cmd or add/sub in register mode
is_writing.d = (currentCmdFSM.q == currentCmdFSM.MOV); //only mov cmd can write to a register aparently
} else if(subclk == 2 && is_reading_inProgress.q == 0 && is_writing_inProgress.q == 0) {
//mark input as read
if(is_reading.q == 1) {
//do reading stuff
//TODO: this
read_reg = 1;
}
} else if(subclk == 3 && is_writing_inProgress.q == 0) {
//reading
if(is_reading.q == 1) {
read_reg = 1; //try to read the register NOW
is_reading_inProgress.d = ~reg_read.done; //wenn fertig -> kein read mehr in progress, sonst: nächsten zyklus nochmal
}
} else if(subclk == 4 && is_reading_inProgress.q == 0 && is_writing_inProgress.q == 0) {
//calculate
//args
mainarg = currentCmd_has_const.q ? currentCmd_const.q : reg_read.out;
case (currentCmdFSM.q) {
currentCmdFSM.MOV:
to_reg = mainarg;
write_reg = 1;
is_writing_inProgress.d = 1;
currentCmdFSM.JEZ: //jump equals Zero
if (acc.q == 0) {
//jump
pc.d = pc.q + mainarg;
}
currentCmdFSM.JNZ: //jump not equals zero
if(acc.q != 0) {
//jump
pc.d = pc.q + mainarg;
}
currentCmdFSM.JGZ: //jmp greater zero
if(acc.q > 0) {
pc.d = pc.q + mainarg;
}
currentCmdFSM.JLZ: //jmp less zero
if(acc.q < 0) {
pc.d = pc.q + mainarg;
}
currentCmdFSM.JRO: //jmp ro (offset) unconditional
pc.d = pc.q + mainarg;
currentCmdFSM.ADD:
acc.d = acc.q + mainarg;
currentCmdFSM.SUB:
acc.d = acc.q - mainarg;
currentCmdFSM.NEG:
acc.d = -acc.q;
currentCmdFSM.SWP:
bak.d = acc.q; //does this work?
acc.d = bak.q;
currentCmdFSM.SAV:
bak.d = acc.q;
}
} else if(subclk == 7 && is_reading_inProgress.q == 0 && is_writing_inProgress.q == 0) {
//increment pc
if(pc.q + 1 >= GNode.ROWS) {
pc.d = 0; //loop over
} else {
pc.d = pc.q + 1;
}
//reset everything
}
}