I'm fairly fresh to this field, so hopefully someone else can come along and give a better answer. I just picked up an iCE40[1] and Cyclone IV dev board[2] and started hacking Verilog.
I know Verilog isn't the best option around, and I'm planning on moving onto something better but for now it was easy to get started, and from what I could see many of those better tools end up generating Verilog so would be nice to be able to debug that.
If you know how to think in logic-gate-terms then FPGAs won't be that difficult I suspect. Essentially either you have plain logic gates where the output is purely a function of the inputs, or you have edge-triggered logic thanks to flip-flops.
Just think of it as writing code that specifies how to wire up set of logic gates, muliplexers and flip-flops.
As a brief example, here's an excerpt from my very first ALU:
wire [8:0] res_and;
wire [8:0] res_xor;
wire [8:0] res_add;
assign res_and = (bus_a & bus_b);
assign res_xor = (bus_a ^ bus_b);
assign res_add = (bus_a + bus_b);
always @(posedge clk)
case (op_sel)
OP_AND: result <= res_and;
OP_XOR: result <= res_xor;
OP_ADD: result <= res_add;
default: result <= 0;
endcase
Here "bus_a" and "bus_b" are wires (which may come from registers), while "result" is a register.
The wires don't hold state, while registers can. The <= means to latch the right side into the left side, in this case on a positive edge of clk. The case statement is just a multiplexer.
At least to me, it feels very much like "writing a circuit".
He designs a Z-80 from scratch, giving Verilog hints / good practice along the way. The design isn't 'complete' -- you have to learn what he's saying and add in the other parts.
It's not a 'trivial' book but if you want to Get Good -- recommended.
Thanks! I feel like I actually understand that verilog code! Do you ever see the "rendered" state on the FPGA, or is that all abstracted away?
Would you recommend one of those FPGAs over another? I know iCE40 from this amazing project (https://github.com/nickmqb/fpga_craft) so maybe I'll just get that!
I also want to refer you to this http://fpgacpu.org/ especially the XSOC links. Jan builds a complete SoC-on-a-FPGA in really 'ancient' fabric (4000-series Xilinx) for Circuit Cellar (articles available on this site). He also hacks a C compiler to emit code for his 16-bit RISC machine (which is very clever).
This is historical, but a 'must read' if you're new to this stuff.
I use the iCE40 with 8K LUTs. It's 'old' but the open-source chain is nice, the vendor-supplied chain is fine, and it's 'big enough' to do fairly serious stuff.
Like the subc compiler I mentioned, the tradeoffs working with an older FPGA like that are ok to me.
GL! p.s. I mostly used the TinyFPGA BX to learn about the iCE40, but it doesn't have enough pins to make even a semi-decent full machine (with wide memory bus) so I use the Lattice HX 8K dev board. It's also easy to use.
You can certainly inspect the synthesized primitives, either graphically post-place&route, or in a netlist which is usually a tangled mess.
Typically you're doing so either because you're curious, you're debugging a timing problem, or you're trying to make sure the toolchains inferred a particular primitive you are trying to use on purpose. It's not terrifically informative otherwise.
I know Verilog isn't the best option around, and I'm planning on moving onto something better but for now it was easy to get started, and from what I could see many of those better tools end up generating Verilog so would be nice to be able to debug that.
If you know how to think in logic-gate-terms then FPGAs won't be that difficult I suspect. Essentially either you have plain logic gates where the output is purely a function of the inputs, or you have edge-triggered logic thanks to flip-flops.
Just think of it as writing code that specifies how to wire up set of logic gates, muliplexers and flip-flops.
As a brief example, here's an excerpt from my very first ALU:
Here "bus_a" and "bus_b" are wires (which may come from registers), while "result" is a register.The wires don't hold state, while registers can. The <= means to latch the right side into the left side, in this case on a positive edge of clk. The case statement is just a multiplexer.
At least to me, it feels very much like "writing a circuit".
[1]: https://www.latticesemi.com/en/Products/DevelopmentBoardsAnd...
[2]: https://www.aliexpress.com/item/32949281189.html