- Hardware & Machines
- Computers and Hardware
- LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
- DMNZ
- Offline
- New Member
-
Less
More
- Posts: 13
- Thank you received: 4
19 Jun 2025 11:34 #330497
by DMNZ
Replied by DMNZ on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
wow, great answer thank you. i was close in my thoughts, since there is no cpu and in rio.c you compile riocomp i thought that there must be something like fixed data frame structure adjusted for each config, but you made it very clear.
do you do anything about noise? like CRC (checksum) maybe for each frame in case it gets corrupted?
do you do anything about noise? like CRC (checksum) maybe for each frame in case it gets corrupted?
Please Log in or Create an account to join the conversation.
- meister
- Offline
- Platinum Member
-
Less
More
- Posts: 579
- Thank you received: 352
19 Jun 2025 17:42 #330523
by meister
Replied by meister on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
no checksum for SPI, it's only for short distance. only the UDP and RS485/RS422 connections have checksum
Please Log in or Create an account to join the conversation.
- meister
- Offline
- Platinum Member
-
Less
More
- Posts: 579
- Thank you received: 352
20 Jun 2025 06:22 #330550
by meister
Replied by meister on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
Here is an output from the generator using the Tangbob configuration as an example:
github.com/multigcs/riocore/tree/dev/doc/example-output/Tangbob
and here is a kind of documentation of the generated gateware:
www.multixmedia.org/Tangbob-Gateware/
github.com/multigcs/riocore/tree/dev/doc/example-output/Tangbob
and here is a kind of documentation of the generated gateware:
www.multixmedia.org/Tangbob-Gateware/
Please Log in or Create an account to join the conversation.
- meister
- Offline
- Platinum Member
-
Less
More
- Posts: 579
- Thank you received: 352
20 Jun 2025 07:12 - 20 Jun 2025 07:12 #330552
by meister
Replied by meister on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
here i can also show the structure of the frames or the rio.v:
this is the 'top' verilog module, the main module:
github.com/multigcs/riocore/blob/dev/doc...ngbob/Gateware/rio.v
there is a block in which the RX frames are split into individual 'output signals':
and a block in which the ‘input signals’ are assembled to the TX frame:
in the case of the Tangbob, the two frames ‘rx_data’ and ‘tx_data’ are passed to the w5500 module, which then takes care of the data exchange with the host computer:
Now let's take a look at the signals from one of the Stepdir module instances:
the input/output signals have the following naming scheme: VAR[DIRECTION][SIZE]_[MODULE_INSTANCE]_[SIGNALNAME]
* VAROUT32_STEPDIR0_VELOCITY : a 32 bit output value for the specified velocity (steprate)
* VAROUT1_STEPDIR0_ENABLE : a 1 bit output value to activate the stepper (enable signal)
* VARIN32_STEPDIR0_POSITION : a 32 bit input value to return the current position (step counter)
the pin signals have the following naming scheme: PIN[DIRECTION]_[MODULE_INSTANCE]_[PINNAME]
however, they can also have suffixes, such as. _RAW / _INVERTED / _DEBOUNCED / ...
this is due to the modifiers pipeline that can be configured for each pin.
this is the 'top' verilog module, the main module:
github.com/multigcs/riocore/blob/dev/doc...ngbob/Gateware/rio.v
there is a block in which the RX frames are split into individual 'output signals':
// PC -> FPGA (330 + FILL)
// assign header_rx = {rx_data[327:320], rx_data[335:328], rx_data[343:336], rx_data[351:344]};
assign VAROUT128_MODBUS0_TXDATA = {rx_data[199:192], rx_data[207:200], rx_data[215:208], rx_data[223:216], rx_data[231:224], rx_data[239:232], rx_data[247:240], rx_data[255:248], rx_data[263:256], rx_data[271:264], rx_data[279:272], rx_data[287:280], rx_data[295:288], rx_data[303:296], rx_data[311:304], rx_data[319:312]};
assign VAROUT32_PWMOUT0_DTY = {rx_data[167:160], rx_data[175:168], rx_data[183:176], rx_data[191:184]};
assign VAROUT32_STEPDIR0_VELOCITY = {rx_data[135:128], rx_data[143:136], rx_data[151:144], rx_data[159:152]};
assign VAROUT32_STEPDIR1_VELOCITY = {rx_data[103:96], rx_data[111:104], rx_data[119:112], rx_data[127:120]};
assign VAROUT32_STEPDIR2_VELOCITY = {rx_data[71:64], rx_data[79:72], rx_data[87:80], rx_data[95:88]};
assign VAROUT32_STEPDIR3_VELOCITY = {rx_data[39:32], rx_data[47:40], rx_data[55:48], rx_data[63:56]};
assign VAROUT1_WLED0_0_GREEN = {rx_data[31]};
assign VAROUT1_WLED0_0_BLUE = {rx_data[30]};
assign VAROUT1_WLED0_0_RED = {rx_data[29]};
assign VAROUT1_BITOUT0_BIT = {rx_data[28]};
assign VAROUT1_BITOUT1_BIT = {rx_data[27]};
assign VAROUT1_PWMOUT0_ENABLE = {rx_data[26]};
assign VAROUT1_STEPDIR0_ENABLE = {rx_data[25]};
assign VAROUT1_STEPDIR1_ENABLE = {rx_data[24]};
assign VAROUT1_STEPDIR2_ENABLE = {rx_data[23]};
assign VAROUT1_STEPDIR3_ENABLE = {rx_data[22]};
// assign FILL = rx_data[21:0];
and a block in which the ‘input signals’ are assembled to the TX frame:
// FPGA -> PC (349 + FILL)
assign tx_data = {
header_tx[7:0], header_tx[15:8], header_tx[23:16], header_tx[31:24],
timestamp[7:0], timestamp[15:8], timestamp[23:16], timestamp[31:24],
MULTIPLEXED_INPUT_VALUE[7:0], MULTIPLEXED_INPUT_VALUE[15:8],
MULTIPLEXED_INPUT_ID[7:0],
VARIN128_MODBUS0_RXDATA[7:0], VARIN128_MODBUS0_RXDATA[15:8], VARIN128_MODBUS0_RXDATA[23:16], VARIN128_MODBUS0_RXDATA[31:24], VARIN128_MODBUS0_RXDATA[39:32], VARIN128_MODBUS0_RXDATA[47:40], VARIN128_MODBUS0_RXDATA[55:48], VARIN128_MODBUS0_RXDATA[63:56], VARIN128_MODBUS0_RXDATA[71:64], VARIN128_MODBUS0_RXDATA[79:72], VARIN128_MODBUS0_RXDATA[87:80], VARIN128_MODBUS0_RXDATA[95:88], VARIN128_MODBUS0_RXDATA[103:96], VARIN128_MODBUS0_RXDATA[111:104], VARIN128_MODBUS0_RXDATA[119:112], VARIN128_MODBUS0_RXDATA[127:120],
VARIN32_STEPDIR0_POSITION[7:0], VARIN32_STEPDIR0_POSITION[15:8], VARIN32_STEPDIR0_POSITION[23:16], VARIN32_STEPDIR0_POSITION[31:24],
VARIN32_STEPDIR1_POSITION[7:0], VARIN32_STEPDIR1_POSITION[15:8], VARIN32_STEPDIR1_POSITION[23:16], VARIN32_STEPDIR1_POSITION[31:24],
VARIN32_STEPDIR2_POSITION[7:0], VARIN32_STEPDIR2_POSITION[15:8], VARIN32_STEPDIR2_POSITION[23:16], VARIN32_STEPDIR2_POSITION[31:24],
VARIN32_STEPDIR3_POSITION[7:0], VARIN32_STEPDIR3_POSITION[15:8], VARIN32_STEPDIR3_POSITION[23:16], VARIN32_STEPDIR3_POSITION[31:24],
VARIN1_BITIN0_BIT,
VARIN1_BITIN1_BIT,
VARIN1_BITIN2_BIT,
VARIN1_BITIN3_BIT,
VARIN1_BITIN4_BIT,
3'd0
};
in the case of the Tangbob, the two frames ‘rx_data’ and ‘tx_data’ are passed to the w5500 module, which then takes care of the data exchange with the host computer:
w5500 #(
.MAC_ADDR({8'hAA, 8'hAF, 8'hFA, 8'hCC, 8'hE3, 8'h1C}),
.IP_ADDR({8'd192, 8'd168, 8'd10, 8'd194}),
.NET_MASK({8'd255, 8'd255, 8'd255, 8'd0}),
.GW_ADDR({8'd192, 8'd168, 8'd10, 8'd1}),
.PORT(2390),
.BUFFER_SIZE(BUFFER_SIZE),
.MSGID(32'h74697277),
.DIVIDER(0)
) w55000 (
.clk(sysclk),
.mosi(PINOUT_W55000_MOSI_RAW),
.miso(PININ_W55000_MISO),
.sclk(PINOUT_W55000_SCLK_RAW),
.sel(PINOUT_W55000_SEL_RAW),
.intr(1'd0),
.rx_data(rx_data),
.tx_data(tx_data),
.sync(INTERFACE_SYNC)
);
Now let's take a look at the signals from one of the Stepdir module instances:
the input/output signals have the following naming scheme: VAR[DIRECTION][SIZE]_[MODULE_INSTANCE]_[SIGNALNAME]
* VAROUT32_STEPDIR0_VELOCITY : a 32 bit output value for the specified velocity (steprate)
* VAROUT1_STEPDIR0_ENABLE : a 1 bit output value to activate the stepper (enable signal)
* VARIN32_STEPDIR0_POSITION : a 32 bit input value to return the current position (step counter)
stepdir #(
.PULSE_LEN(108),
.DIR_DELAY(18)
) stepdir0 (
.clk(sysclk),
.step(PINOUT_STEPDIR0_STEP_RAW),
.dir(PINOUT_STEPDIR0_DIR_RAW),
.velocity(VAROUT32_STEPDIR0_VELOCITY),
.enable(VAROUT1_STEPDIR0_ENABLE & ~ERROR),
.position(VARIN32_STEPDIR0_POSITION)
);
the pin signals have the following naming scheme: PIN[DIRECTION]_[MODULE_INSTANCE]_[PINNAME]
however, they can also have suffixes, such as. _RAW / _INVERTED / _DEBOUNCED / ...
this is due to the modifiers pipeline that can be configured for each pin.
Last edit: 20 Jun 2025 07:12 by meister.
Please Log in or Create an account to join the conversation.
- DMNZ
- Offline
- New Member
-
Less
More
- Posts: 13
- Thank you received: 4
21 Jun 2025 08:35 #330625
by DMNZ
Replied by DMNZ on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
ok. i think i got it. you create a bus with width of the data frame, clock the data in/out via SPI, and map each peripheral to a certain bits in the bus to use as input/output control registers. very elegant, simple and lightweight, the only inconvenience it needs driver rebuild for peripherals changes.
compared to mesa which uses memory map with peripherals mapped to addresses in the memory and need cpu parsing to read write addresses. but this allows dynamic configuration of the driver.
compared to mesa which uses memory map with peripherals mapped to addresses in the memory and need cpu parsing to read write addresses. but this allows dynamic configuration of the driver.
The following user(s) said Thank You: meister
Please Log in or Create an account to join the conversation.
- PCW
-
- Away
- Moderator
-
Less
More
- Posts: 18760
- Thank you received: 5182
21 Jun 2025 15:42 #330638
by PCW
Replied by PCW on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
compared to mesa which uses memory map with peripherals mapped to addresses in the memory and need cpu parsing to read write addresses. but this allows dynamic configuration of the driver.
I should note that a CPU is not required with HostMot2 and no interface CPU is used in PCI, SPI or EPP
interfaced configurations. Interface CPUs are only used with Ethernet, USB, and serial interfaced
configurations to support the more complex wire protocol.
I should note that a CPU is not required with HostMot2 and no interface CPU is used in PCI, SPI or EPP
interfaced configurations. Interface CPUs are only used with Ethernet, USB, and serial interfaced
configurations to support the more complex wire protocol.
The following user(s) said Thank You: tommylight, meister
Please Log in or Create an account to join the conversation.
- DMNZ
- Offline
- New Member
-
Less
More
- Posts: 13
- Thank you received: 4
22 Jun 2025 00:48 - 22 Jun 2025 01:30 #330667
by DMNZ
Replied by DMNZ on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
yes. sorry. i did not mean to compare anything in a bad way. i only meant Ethernet. in fact, RIO also needs CPU for ethernet, it just placed outside of fpga in UDP2SPI Bridge with STM32.
i hope i got the difference in data frame at least, RIO has fixed static data frame while Mesa has dynamic data frame configured by reading IDROM at the driver initialization.
i hope i got the difference in data frame at least, RIO has fixed static data frame while Mesa has dynamic data frame configured by reading IDROM at the driver initialization.
Last edit: 22 Jun 2025 01:30 by DMNZ.
Please Log in or Create an account to join the conversation.
- meister
- Offline
- Platinum Member
-
Less
More
- Posts: 579
- Thank you received: 352
22 Jun 2025 12:15 #330683
by meister
Replied by meister on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
small correction , udp2spi is outdated, and the W5500 is controlled directly by the FPGA
Please Log in or Create an account to join the conversation.
- epineh
- Offline
- Senior Member
-
Less
More
- Posts: 56
- Thank you received: 18
22 Jun 2025 14:00 #330691
by epineh
Replied by epineh on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
I'm glad you guys know what you're talking about, most days I'm happy if I tie my shoelaces correctly
Any hoo, I was wondering about shift registers, in that should I use hardware means to clear all outputs? I haven't had a lot to do with them but I understand that the output shift registers can have undefined states at startup and possibly turn on randomly. For the most part probably not an issue but if you are using them for things that shouldn't turn on at startup, tool release, turret clamp, Z Axis brake, spindle gearbox and so on. My friend (Babinda01 on here) has mentioned using a simple resistor/cap combination connected to the shift register clear pin to reset the output registers at power up, which sounds like a good idea instead of burning up a pin from the FPGA.
Also just checking that I can assign ModBus pins for output for a second FPGA and also additional ModBus pins for, well normal Modbus things? My understanding is the ModBus to the second FPGA is just for doing that, and not for adding traditional ModBus devices, so I should design a second "standard" Modbus connection.
Cheers.
Russell.
Any hoo, I was wondering about shift registers, in that should I use hardware means to clear all outputs? I haven't had a lot to do with them but I understand that the output shift registers can have undefined states at startup and possibly turn on randomly. For the most part probably not an issue but if you are using them for things that shouldn't turn on at startup, tool release, turret clamp, Z Axis brake, spindle gearbox and so on. My friend (Babinda01 on here) has mentioned using a simple resistor/cap combination connected to the shift register clear pin to reset the output registers at power up, which sounds like a good idea instead of burning up a pin from the FPGA.
Also just checking that I can assign ModBus pins for output for a second FPGA and also additional ModBus pins for, well normal Modbus things? My understanding is the ModBus to the second FPGA is just for doing that, and not for adding traditional ModBus devices, so I should design a second "standard" Modbus connection.
Cheers.
Russell.
Please Log in or Create an account to join the conversation.
- meister
- Offline
- Platinum Member
-
Less
More
- Posts: 579
- Thank you received: 352
22 Jun 2025 16:15 #330707
by meister
Replied by meister on topic LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
in general, FPGAs and microcontrollers also have undefined outputs at startup, so there should be a pullup or donw resistor on each one (all are floating by default).
in the case of the shift register, i'm not sure right now
they are always set to output and i thought that they are always set to 0 at first.
you can use as many of the same plugins next to each other as you want and as much space as you have
for modbus (frame IO), it should always be on the master FPGA,
I'm not sure if it works properly on a slave.
For encoders with index I have to check again, because there is an inout pin.
the uart plugin for connecting several FPGAs doesn't have much to do with modbus, it can simply use RS485, so that's not a problem either.
in the case of the shift register, i'm not sure right now

they are always set to output and i thought that they are always set to 0 at first.
you can use as many of the same plugins next to each other as you want and as much space as you have

for modbus (frame IO), it should always be on the master FPGA,
I'm not sure if it works properly on a slave.
For encoders with index I have to check again, because there is an inout pin.
the uart plugin for connecting several FPGAs doesn't have much to do with modbus, it can simply use RS485, so that's not a problem either.
The following user(s) said Thank You: epineh
Please Log in or Create an account to join the conversation.
- Hardware & Machines
- Computers and Hardware
- LinuxCNC-RIO - RealtimeIO for LinuxCNC based on FPGA (ICE40 / ECP5)
Time to create page: 0.202 seconds