This a project for a group of folks working to reverse engineer the Barco NX-4 LED panels.

Similar projects worth following
Community project for reverse engineering the LED tile we all got cheap! Feel free to join if you want to share progress and work together!

If you want to be approved for membership, write about your progress (or effort) in the comments.

SuperConference folks: Welcome! Hopefully what we have is useful to you, please add information here if you figure something out!

We are updating the project details with information as we go.

The story so far...

Update: Row scanning working.


  • Building and loading via JTAG new firmware into the main FPGA. This firmware is starting from scratch, we don't understand much yet about the default Barco firmware, although we've dumped it.
  • We understand the majority of how the board works and can display pixels (now on all rows), and communicate via the IN and OUT connectors (various protocols partially done, UART finished and working)
  • Scanning out pixels to the main LEDs  (and the 3 self-test LEDs on the back) 


  • Don't know anything so far about the native Barco data format for driving the tiles without reflashing them first; this is going to be difficult without having a working Barco rig (controllers and head end) or insider info. 
  • Don't really know what the CPLD does internally, there's some communications between the Spartan and the CPLD that are captured but not figured out. It may not matter too much.


  • Daisychaining tiles (e.g. via LVDS)
  • Temporarily (until powercycle) reprogramming a tile is easy enough with a JTAG interface, but a small amount more work is required to write a permanent image to the parallel flash onboard.
  • A variety of housekeeping stuff; fan control, understanding the dot-correction calibration eeproms, and so on.

Panel LEDs

Contributors: @Richard Aplin, @modder_mike

The panel is divided into twelve segments of 16x6.  Each segment is driven by three Texas Instruments TLC5941 16-channel LED drivers.  Each driver controls one color of the tri-color LEDs.  The three drivers have their serial data cascaded, with the first controller in the cascade being Red, then Green, then Blue.  Anode voltage is switched to each of the six rows of LEDs in sequence via a transistor controlled by the CPLD.

The 96 LEDs in the segment are controlled by only 16 driver channels by multiplexing the rows' LED anode voltage (TLC5941 switches the cathode side).  Each of the 36 rows has a transistor between the panel's +4.5V input and the LED anodes.  All like-numbered rows' transistors are connected to a common control pin on the CPLD.  The CPLD cycles through them in sequence as instructed by the FPGA, synchronized with the incoming display data.

Take care when rewriting HDL for the FPGA.  Because the LEDs are expecting to be run at only 1/6 duty cycle, they may theoretically be damaged if they are not cycled as quickly by user HDL.  (For experimenting, consider using the calibration LEDs on the rear of the panel, which are not multiplexed.)

There are three I2C devices on the LED panel, an EEPROM, a temperature sensor and an ambient light sensor used for brightness calibration.

Most of the LED driver control pins are brought out to the data connector by way of two buffers.  The remaining signals (SOUT for each driver string) are fed to the CPLD.

... Read more »

svf - 667.94 kB - 11/26/2017 at 12:27



Same as "Simple_NX4_11_25.bit" in SVF format; works for me (richaplin); can boot a tile into r,g,b all row display. Although built from same src this SVF file is not quite the same as the one Spacecoaster posted. Give it a shot.

svf - 667.94 kB - 11/26/2017 at 05:15



Same program as posted by SpaceCoaster on github "simple_nx4_8eab499.svf" but as a .bit file, compiled independently by rich aplin, confirm it works the same for me as him - I can power up my tile, load this .bit, and get all rows showing sequential r,g,b

bit - 165.34 kB - 11/26/2017 at 05:05



SVF for programming simple_nx4. Displays vertical BRG stripes.

svf - 665.18 kB - 11/24/2017 at 21:39


A sigrok / PulseView compatible logic readout of the first data burst present on the CPLD-FPGA communication pins as the module starts up.

sr - 4.48 kB - 11/24/2017 at 19:32


View all 16 files

  • 1 × Xilinx XC3S250E FPGA, Spartan-3E, 250K gates, TQFP-144
  • 1 × Cypress CY7C1041D Static RAM, 4Mbit (256K x 16), TSOP-44
  • 1 × Xilinx XC9536XL CPLD, 800 gates, QFP-44
  • 37 × Texas Instruments TLC5941 LED driver, low-side, 16-channel, QFN-32 (5x5)
  • 2 × Texas Instruments SN74LVC16244A Buffer/driver, tri-state, 16 channel (4x4), TSSOP-48

View all 14 components


    kevtris03/30/2018 at 04:06 7 comments

    Well I managed to snag an NX-4 control box off ebay for 100 bucks last week!  It was a total pig in a poke (kind of like the LED tiles were) but it seems to work, at least enough to drive the displays!  I have it showing test patterns and stuff now using it.

    The wiring is now figured out for the cables, and it works like this:

    (8 pin "out" connector -> 9 pin "in" connector)

    1 - > 1  (+24V)

    2 -> 6 (GND)

    3 -> 7 (out data to display positive)

    4 -> 3 (out clock to display negative)

    5 -> 5 (in data from display positive)

    6 -> 2 (out data to display negative)

    7 -> 8 (out clock to display positive)

    8 -> 4 (in data from display negative)

    It appears the cable from the NX control box to the tile, and the cable between tiles is the same.  I guess this would make sense.

    Inside the NX control box proper is a huge 24V, 15A(!) power supply.  It has an IEC receptacle on it, but unfortunately it is only for 240V operation.  It will not run on 120V.  The supply is extremely high quality and has a very small for this power level planar transformer inside and a PIC micro.  It also has I^2C for some reason.     I cannot use the existing supply so I am just hooking my bench supply up for now and running a single tile.

    A quick scoping reveals the clock going to the display is 50MHz which is a bit higher than I was expecting but not too far out of the ordinary I guess.   I initially didn't twist my pairs because I was lazy, and it resulted in it not working very well at all.

    Holding the wires together with my finger seemed to make it work a lot better and I started to get actual data on the display!  Interesting how the word "up" was showing up in green at the bottom there.

    Finally, after doing it right and twisting the pairs, it seemed to be working properly.

    There's a two position dip switch on the board with the text "sw this way for normal operation" with an arrow.  If both of the switches are in the down position, it will display a moving white grid pattern that marches down and to the right one row/column every 300ms or so.  I assume this is so you can tell if you have arranged the tiles properly in the array and have not turned one or more of them.

    If one switch is up and the other is down, you get the "soft back up" message with the marching row/column behind it.  I am not sure what this means but it is probably documented in the manual. 

    Clicking both up in the "normal" position, results in all LEDs glowing a dim grey, I suspect for LED testing or similar.  On this panel all LEDs appear to light and with absolutely uniform brightness. 

    There's a ton of LEDs on the control PCB and they all flash various patterns.  I am not sure what they mean but there's four LEDs near the connector to the indicator LEDs for the box which indicate status.  One flashes yellow about once a second (sometimes it will flash twice in a pattern) and the other flashes orange 3-4x a second.

    Hardware on the control board is pretty brute force.  There's an ARM CPU, Stratix GX FPGA (with 8 serdes, two per HDMI connector to link the control boxes together as shown in the docs), 6 128Kbyte 15ns SRAMs, a maxii CPLD, and a bunch of power supplies.

    On the bottom is a 4mbyte parallel flash ROM and a MAX232 RS232 transceiver.  There is also a mini USB connected to the ARM, but I have not plugged anything into it yet.  There are 3 big honking 9A 30V polyswitches to limit current to each string of three LED panels as well.

    That's about it for now, I just got this box today and had to do a quick play around to see if I could get it to light up one of the LED panels, and it did!   I hope it isn't too tough to hook this up to an FPGA and do some heavy duty reverse engineering of their protocol now.

    Finally, a crappy pic of the board inside the box.

  • A Slight Disaster and More Info

    kevtris12/05/2017 at 11:12 4 comments

    I decided to try and feed the display so more LFSR goodness, and I managed to get the link light to stay lit, and the fan kept spinning.   Buoyed by this success, I kept at it and could keep the LED/fan on longer.   Each time the FPGA was reset, the LEDs would flash on briefly with a different pattern too.

    But, after awhile the status LED started doing a double flash.   This means I blew away the main application firmware somehow.  Whoops.   Checking the rental install PDF, this means that the board is running on backup firmware.  I will eventually desolder the flash on this board anyways for testing, and I will replace it with two .5mm FFC connectors to hook it up to my tester.

    In the mean time, I will remove the flash from another board, bend pin 10 (/WE) up and reinstall, then solder /WE to 3.3V to prevent the flash getting blown away with subsequent poking.  I should've done this from the start, but these things happen.

    I was doing a bunch of poking around the barco website, and their "director toolset" is available for download.  Inside the install file, I found all the various firmware images for what looks like every (or nearly so) LED product they make and have made.  There's no registration or other requirements it seems and the installer is there for download.

    I used winzip to unpack the installer without running it.

    In the installer it lives at:  /install/install/installerdata/disk1/instdata/

    in /$IA_PROJECT_DIR$/

    in the above zip: /flashfiles/


    Inside was the firmware/software for these tiles.   The FPGA bitstream and the microblaze code are separate files.  There's a couple versions of each for the NX4 stuff.   These appear to have a bit more metadata in them but otherwise look similar to the stuff found in the parallel flash.

    The interesting files are named:

    Nx firmware_ctrl 4.0.0.hwr    (434K)

    Nx firmware_mod 4.0.0.hwr (166K)

    Nx program 4.0.0.swr           (129K)

    Nx program_backup 2.0.0.swr (57K)

    Nx program_mod 4.1.0.swr  (75K)

    The documentation pdf that is on the director toolset link is also kind of interesting;  it explains things like calibrating the display using a camera, manually, etc. as well as the kinds of diagnostic information available.   These displays are also capable of gap compensation where it dims the pixels on the inside vs. the outside to make gaps less noticeable.. neat.

  • I^2C for Fun and Profit

    kevtris12/03/2017 at 01:22 2 comments

    After some messing around, I managed to capture the entire I^2C sequence on startup, and now have a complete log of what happens on the bus.

    Looks like it initializes everything and then reads a bunch of the data out of the EEPROM on the LED board which isn't too surprising.  Every 2 seconds, it reads the two temperature sensors.    I will have a link below to the entire data log in human readable format.  Here's some highlights, though, with my notes:

    At the start, there's a bunch of small 1-4 byte reads out of the two EEPROMs, most likely reading version/model information.


    SUBADD: 32 ACK 32 ACK 32 ACK 32 NACK



    SUBADD: 00 ACK 12 NACK


    SUBADD: 01 ACK 00 ACK

    SUBADD: 32 ACK 32 ACK 32 ACK 32 NACK


    SUBADD: 01 ACK 04 ACK

    SUBADD: 00 ACK 05 NACK


    If you know about I^2C, the above should be self-explainatory.  If not, here's how a typical EEPROM read works:  First, the start condition tells the chip a transmission is happening;  then it sends the device address (50h is the control board's EEPROM, 51h is the display board's).   The direction of the following command is sent (read or write) then the sub-address.

    For the first transmission the address 0F10h is sent.   To read from a location in the EEPROM, you first tell the chip you're going to write and specify an address, then repeat the start condition with read.   This is kinda hacky IMO but it is how it works.

    After this, the EEPROM will spit out 1 or more bytes.  Your code has to ACK each byte except the last;  at which point you NACK to tell the chip you're done with it, and finally end with a stop condition.

    So right off the bat, they are reading locations 0F10h-0F13h from the control board EEPROM, then they read from 0F14h-0F15h.  

    They then read from the EEPROM on the LED board in a similar manner, except it's reading addresses 0100-0103h and then 0104h-0105h.

    There's some more transfers from the two EEPROMs and then they initialize the light sensor:

    SUBADD: 80 ACK 03 ACK


    SUBADD: 81 ACK 00 ACK


    Going by the datasheet for the TSL2560CL ( )

    Device address 39h is the sensor, then they write 03h to register 00h  which turns the chip on.  Register 00h is the configuration register.

    Next, they write 00h to register 01h.  This is the "timing register".  It sets the gain to 1x, and sets integration time to 13.7ms (the longest integration time available).

    After this, the two temperature sensors are initialized:

    SUBADD: 01 ACK 00 ACK


    SUBADD: 05 ACK 00 ACK


    SUBADD: 02 ACK 4B ACK 00 ACK


    SUBADD: 03 ACK 55 ACK 00 ACK


    SUBADD: 01 ACK 00 ACK


    SUBADD: 05 ACK 00 ACK


    SUBADD: 02 ACK 4B ACK 00 ACK


    SUBADD: 03 ACK 55 ACK 00 ACK


    Device addresses 48h and 49h are the two temperature sensors.   Both are programmed identically.   The meaning of the writes is:

    01h 00h:   Write 00h to register 01h-  this is the configuration register.   This selects temperature sensor 0 (the internal temperature sensor), clears the error queue, and turns the chip on.

    05h 00h:  Write 00h to register 05h-  this is the secondary configuration register.  It's simply zeroed out since this chip does not have the "conversion start" pin.

    02h 4bh 00h:  Writes 4b00h to register 02h- this sets the lower hysteresis trip point for overtemp. ...

    Read more »

  • The Adventure Begins

    kevtris12/02/2017 at 10:01 0 comments

    Well, I have one of the modules connected up to my general purpose FPGA reverse engineering / ROM emulation rig.  There's no ROM emulation yet but I can feed it arbitrary data and receive data from the display.

    For testing, I sent it LFSR data (41 bit LFSR) at various speeds from 100KHz to 10MHz or so and so far nothing happened.  This isn't too surprising I guess.  It most likely has some kind of complex packet structure to turn the display on and configure it.

    I also hooked the FPGA's programming pin up that reconfigures it so I can reboot the board without having to power it down and up again.   This lets me catch anything that happens on startup.      While I was soldering wires, I connected the I^2C bus to my board as well.

    I will record the entire I^2C startup sequence (it's long and takes a few seconds) along with the data that comes back out of the chip on startup.   There's 4mbytes of RAM which should be plenty for this application.

    Eventually I want to replace the RAM/ROM on the board with an adapter for this emulator board and probe memory as it runs.

    One interesting thing is if you reset the board, some random rows of LEDs will flash on and off quickly as the CPLD is sent the control signals.  This doesn't happen from poweron, just if you reset the FPGA.

    The data coming back from the display seems to be boring and so far not too interesting.  It's the same data that it spits out of the output jack.   It is a 10 bit repeating pattern: 1111111111111111_0001110101_0001110101_0001110101.    If the data is interpreted such that the initial 0 is a start bit, the data becomes 0011_1010 with an extra "1" stop bit.  This is ASCII for the backslash (\) character.  I guess the data format might be basically RS-232 like in nature but synchronous instead of asynchronous.

    Changing the input clock over the entire 10KHz to 10MHz range resulted in the same exact synchronous data stream coming out.  Maybe the display is looking for text/data strings?  That'd be pretty interesting if so (and I suspect they will be present in the parallel flash).   It most likely is packetized format, and there will be commands to turn it on, check the calibration, read the temp sensors, etc.  As it is, the I^2C is not accessed again after startup so it is obviously sitting and waiting for commands before it starts doing stuff (tm).  If it is packetized it probably is checking for a hash/checksum too and it will not accept the data unless this is present.

    Here's a quick dump from signaltap.   GPIN10 and GPIN11 are the differential output from the display's input connector, GPIN12 and GPIN14 is the I^2C.  There was a gap in the I^2C data so I moved the time slider to the point where a new I^2C transaction starts.  The differential return data idles with GPIN11 high and GPIN10 low (the 111111 part above).  The data logging was triggered on the rising edge of the clock I am feeding the board so it just looks high all the time (GPOUT2 and 3 is the clock,  GPOUT 0 and 1 is the data).

  • First Impressions

    kevtris11/30/2017 at 11:15 5 comments

    I received some of the displays today and decided to poke around with them a little bit.

    There is quite a bit of activity on start up.   The I^2C bus is read for a few seconds,  the fan starts and stops, and various other things happen.

    Watching the data bus to the RAM/flash, they are constantly hitting the RAM.   At startup, you can see what I suspect is the I^2C data being written into RAM, presumably for the dot correction.

    I hooked some wires up to the back side of the control PCB and brought them out and did a little messing around.   It looks like pins 3/8 on the input connector is the clock.  Feeding it a 1MHz differential clock results in a similar clock emerging out of the output connector (pins 4/7).   When the clock is provided, a repeating synchronous data pattern is present on pins 3 and 6 of the output connector.

    If the clock is provided on startup, the output pins on the input port (pins 4/5) spit out a pretty hefty string of data shortly after power is applied.  I am not sure what data is coming out, but it definitely is something.   There doesn't seem to be a repeating pattern looking on the scope but I am unsure what it is.

    Whatever data is fed into the Din pins (5 and 8) of the output connector seems to simply be passed through to the Dout pins (4/5) on the input connector when it's sitting there during normal operation.

    The passthrough appears to be combinatorial since I can feed nearly anything in and it comes out exactly the same.  The data is not sampled on the clock or anything (i.e. I can feed it a higher frequency clock and it simply passes it through).

    During the startup, it switches the outputs between passthrough and internal state (during the data it sends synchronous with the input clock) then it switches back to passthrough.

    The output clock appears to shut off (on the output connector) when the internal state is being dumped, then it starts up again afterwards when it's in passthrough mode.

    It seems that the FPGA basically passes clock/data through unless it wants to talk to the host.  I guess this makes sense. 

    I was thinking the next step might be to make a small breakout board to replace the SRAM/flash on here and connect it to my ROM emulator device to start substituting data and seeing what addresses are being read/written (as well as being able to dump the RAM contents).   This would also let me watch the bus to see what the CPU inside is doing.

    The access speed seems to be pretty fast, 25 or 50ns, so I might have to swap out the 40MHz oscillator with a 10MHz one or similar to slow it down enough so that my ROM emulator (a RAM + FPGA basically) can keep up.  We'll see.

  • Data is Beautiful

    modder_mike11/25/2017 at 06:20 7 comments

    After @kevtris posted the image of the font hidden in the image file, I thought it might be fun to poke around in there some more and see what could be seen.  I started by mapping out the following data areas (defined as "long" stretches of bytes other than 0xFF):

    0x000000-0x000003: FF FF FF FF (4 bytes)
    0x000004-0x029503: FPGA boot bitstream (169216 bytes)
    0x030002-0x03D407: Something (54278 bytes)
    0x040000-0x0400BB: Something (188 bytes)
    0x09006C-0x0A3513: Something (79016 bytes)
    0x0B0000-0x0B004B: Something (76 bytes)
    0x0D6AFC-0x113513: Something (248344 bytes)
    0x120000-0x1200D7: Something (216 bytes)
    0x1400B8-0x141B05: Something (6734 bytes)
    0x142B00-0x144605: Something (6918 bytes)
    0x145600-0x147105: Something (6918 bytes)
    0x150000-0x1500A7: Something (168 bytes)

    There are three large blocks of data, and in each area can be found a copy of the font that @kevtris documented.  Presumably these are firmware images.

    The font was found with bit spatial analysis (a term I'm using because I don't know the proper term for this) using yy-chr.  That got me thinking, what else might we figure out by looking at the dumped image visually?  So I mangled the data a bit and came out with the following grayscale representation:

    Interesting.  If we know the first band at the top is the FPGA's boot bitstream... then the band in the middle sure looks like another one - but upside down.  The data seems to back this up - starting at 0xFFFFF and continuing backward to 0xD6AFC, there seems to be another 169220 bytes of bitstream there.  I ran a diff between the two bitstreams, and though they look similar, about half of the bytes are different - and since most of the bitstream is zeros, that means there are a whole lot of differences.  But they are both undoubtedly the same type of data:

    With that chunk removed from the 248K of continuous data noted above, we end up with 79124 bytes - much closer in size to the other two similar-looking sections.  Given this, the presence of the font in all three places, as well as a set of strings in all three, it seems that the flash contains two bitstreams, three firmwares, and some small chunks of other data interspersed here and there.  Neat!

    [Edit] Addendum.  The reversed bitstream is a documented feature of Spartan-3E.  It is part of its MultiBoot function.  On first powerup, the FPGA loads a bitstream starting either at the bottom of its address range and reading up, or from the top of its address range and reading down, depending on the configuration of the bitstream header.  Then once this is loaded, by toggling the FPGA's MultiBoot Trigger signal, the FPGA is instructed to reconfigure itself from the opposite end of the memory in reverse order.  Except as you see here, the second image is at 0x0FFFFF, which is not the end of the flash address space, so the programmer seems to have done something interesting with the memory architecture.  It is possible that the flash configurator (iMPACT or similar) was configured for a 1M flash, and the programmer appended additional data after the end of the MultiBoot bitstream, because I can't otherwise figure out how to get iMPACT to override the start address for the second image...

    The dual bitstream function is often used as a safety feature for field-upgradeable systems.  One of the two is protected, and the other is allowed to be written by user software.  If the upgrade goes poorly, there is always a known-good bitstream to bring up the FPGA so that it can be attempted again, and you don't have to go find the JTAG cable.  Another neat feature that Barco may have taken advantage of.

  • Some words on CPLD startup

    modder_mike11/24/2017 at 19:34 1 comment

    I took some logic analyzer plots of the startup sequence of the unknown FPGA-CPLD communication pins.  I don't know what any of it means yet, but maybe it will be of use to someone.

    This plot is taken in timing analysis at 1us/sample, just to show what the whole first two seconds of startup looks like.  There is movement up to about 600ms, then basically nothing as the module goes into 'no incoming signal, just sit here and blink' mode

    I have zoomed in on most of the transitions, and there's not really a lot going on at most of them, with the notable exception of the two little spikes around the second division.  There's actually quite a lot going on around there.  The next two plots were taken in timing analysis at 2ns/sample to adequately capture the clock rate.

    Here's the tail end of the first data blip.  Pins 2, 3 and 5 have relatively constant transitions with occasional rapid bursts.  Pin 6 displays serial data, as theorized in another thread this is likely SOUT data from one of the LED strings.  Pin 8 has a fairly regular clock, but this is interesting - during this startup time, the clock measures around 27MHz, but it jumps to 40MHz when the module goes idle.  What the point of this would be, I don't know.

    The second blip looks similar but the data on Pin 6 is different, the FPGA must be asking the drivers a different question.

    I have posted a Sigrok/PulseView-compatible dump of the first data burst to the Files section.  It is missing the data for PIN41 which was lost when my analyzer crashed before the dump finished, but PIN41 doesn't do anything during this time anyway.  Hopefully someone can make some sense of this.

  • Simple NX4 Test Program

    SpaceCoaster11/24/2017 at 18:30 13 comments

    I have a simple NX4 test program which displays a BRG pattern on the tile. I will update the program as we discover more secrets. Dot Correction implementation is next on the list.

  • Initial thoughts

    kevtris11/23/2017 at 04:30 1 comment

    Well I got on the bandwagon and bought 70 of these modules.  I hope they all or mostly all work.  Anyways, I have been poking through the ROM images posted, and I found a font inside of the parallel flash.  It lives around 83180h or so (I will check for more than 1 copy of it... the install PDF claims there's backup firmware in the flash in one of the troubleshooting sections).   The font looks like this:

    I used yy-chr to grab it.  Interesting that it's 6 pixels high, I guess this is so they can fit 6 lines of it on one of the modules. 

    Was poking through the EEPROM_panel file too, and it seems to be calibration data and possibly pixel order data too.  There's several distinct sections of data.     The EEPROM_control file is kind of weird, there's obviously data in here but it doesn't seem to be calibration data, I am not sure what would be living in here.   I guess it could be a local copy of adjusted calibration data maybe?

    My current working theory is on bootup, the controller will fetch the panel's calibration and control data out of the EEPROM_panel chip and load it into the SRAM (after some kind of processing and possible calibration with the light sensor/LEDs).  I guess over time the panel could recalibrate itself too.   The SRAM most likely holds the frame buffer as well, and during scanout it reads the pixel data and calibration data and does the fixup.

    Reading literature for other similar LED walls, they do a 3*3 matrix multiply for every single pixel to effect the calibration.  I am not sure if this does it, but I wouldn't be surprised if it does.   It might be interesting to creatively corrupt the RAM contents as it runs to see if anything happens.

    Because there is a font in the ROM, this thing most likely has some kind of diagnostics it can perform.

  • OpenNX4 source + binaries posted!

    Richard Aplin11/15/2017 at 07:00 14 comments

    A big chunk of NX4 Xilinx code (with precompiled binaries and python code to talk to the tile over UART) just posted: 

    Currently the code supports taking to a host over UART (e.g. loading images onto the NX4) and python code is provided to make it do stuff. 

    It's currently in an 'advanced user' state, there remains one vital thing to figure out on the NX4; how to get the row scanning working (i.e. how to get the CPLD on the LED driver board to play ball). 

    Once we get that sorted out we're fairly far towards a working, daisy-chainable video tile, controllable by a variety of hosts (Ardunio, embedded linux, etc) using a variety of protocols and doing a variety of 'intelligent display' tricks. 


    We should be able to end up with something you can flash onto a tile in a minute or two with a cheap JTAG adapter, and accepts further firmware updates over the wire

    The blocker right now is the CPLD (not) doing row scan... 

    Here's a really, really bad photo where I'm moving the camera so you can see the scanned image (original inset).   The tile itself seems high quality; you can get excellent intensity gradients on it, and the 1/6 scan rate w/12-bit CC drivers is a luxury.

View all 23 project logs

Enjoy this project?



Jay Greco wrote 09/05/2018 at 04:43 point

@Richard Aplin I'm finally digging into the panels I bought almost a year ago. I'm game to tune up & debug the WS2812B functionality in your OpenNX4 repo (sounds like a fun excuse to brush up on my verilog). I noticed in these comments that you got row scanning working but it doesn't seem to have made it into a git commit. Any chance you'd be willing to push those changes?

  Are you sure? yes | no

modder_mike wrote 07/26/2018 at 01:58 point

Can someone with a control box remove the power supply and take some photos of the top side?  I took mine apart and took the board out to work on it.  Then I walked away for a couple of months, and now I can't remember how all the hardware holding the transistors to the case was installed :(

  Are you sure? yes | no

beauburrows wrote 02/22/2018 at 23:26 point

The NX Control Box I bought arrived. The power supply seems to have an issue (relay clicks on but no output voltage), but just supplying the main board with 24v directly appears to work fine. It boots up and gives some status LEDs, and a fan error LED (which I believe is because it's getting no fan status from the power supply). Let me know if anyone would like me to run any tests with it.

  Are you sure? yes | no

Chankster wrote 02/01/2018 at 02:01 point

There's a control Box up on ebay if anyone is interested in making an offer:

  Are you sure? yes | no

David Kuder wrote 02/09/2018 at 04:06 point

And even that only gets you so far, gains you a powersupply and basically a hub for driving 9 tiles to form one panel. Still needs a processor to feed it data. $99 isn't bad for a powersupply that can run 9 tiles though.

  Are you sure? yes | no

beauburrows wrote 02/09/2018 at 20:07 point

I just bought one of these. Nice tip! I have enough panels to build a 3x3 array and do some tests. If anyone has any requests for tests let me know.

  Are you sure? yes | no

SpaceCoaster wrote 12/01/2017 at 03:48 point

I have made some progress on a simple image display program, called image_nx4. The code is on GitHub at It needs reloading until it happens to start on the correct row. It is a row synchronization problem. Obligatory Hackaday jolly wrencher and Pac-Man images at

  Are you sure? yes | no

Jac Goudsmit wrote 11/27/2017 at 18:03 point

I was at the reverse engineering meeting at Hackaday and just found this project in my feed. You made an amazing amount of progress on this!

I read through various Barco spec sheets and other information on the evening after the meeting and immediately noticed that Barco calls the cables "LVDS cables". And apparently I was right in my assumption that there are 3 LVDS channels in the cables.

Also from the way these are hooked together I started making up some theories about how I would design the hardware myself and that's pretty much how far I got. I wish I had more time for this but I'll be following this closely. Incidentally, I was careful not to do anything destructive with my panel; it should still be in the same state as before we took it apart. I hope maybe one day I can contribute to make it into something useful, or perhaps I'll donate my panel back to a joint project if I think it's a worthy cause.

I starred your Github and I'll be watching that too. Once again: Great work!


  Are you sure? yes | no

SpaceCoaster wrote 11/26/2017 at 17:07 point

A couple of interesting links for driving an older Barco panel and for an fpga driving an led panel from @Mike Harrison. Interesting stuff, not sure if it directly relevant but good to see a teardown of similar technology and concepts.

  Are you sure? yes | no

Mike Harrison wrote 11/26/2017 at 19:34 point

Not hugely relevant as these cover panels with simple shift-register drivers, and the binary-code modulation needed to get greyscale,  not the internal-PWM drivers on the Barco panels, which are a lot simpler to use.

  Are you sure? yes | no

Richard Aplin wrote 11/26/2017 at 12:21 point

row scanning works. This image has it rotated around by 3 lines (and image is sideways) but it's basically good. Thanks Spacecoaster! 
The streaking on the image is my camera sensor freaking out cos I have the tile dot correct set to 50 (out of 63) and it's pretty bright.  The CPLD bootup handshake I've clearly still got wrong cos my code doesn't cleanly boot (i.e. still requires the psychedelic test program (or SpaceCoaster's simple_nx4) to be run once first before you get any leds lighting... It's all a bit messy but we understand most of the tile now I'd suggest. 

  Are you sure? yes | no

SpaceCoaster wrote 11/26/2017 at 13:46 point

Congrats! I found that the rate at which the CPLD is toggled was important. Try attaching it to different clock rates. Also, we must be missing a way of setting the  auto increment counter back to zero. I would think it would happen at the start of every frame to ensure that we stay in sync. Perhaps cpld_p2 is an auto-increment enable and setting it low will reset it. It should be possible to test by sending a partial frame and then pulling it low then high and seeing if we stay in sync.

  Are you sure? yes | no

kevtris wrote 11/23/2017 at 04:19 point

I found a font in the parallel flash ROM.  It seems to be around 8*6 pixels per character or so.  It lives around A3180 or so.  I have a picture of it.

  Are you sure? yes | no

SpaceCoaster wrote 11/23/2017 at 04:59 point

if you find out how to post an in-line image then tell me!

  Are you sure? yes | no

Mike Harrison wrote 11/23/2017 at 11:20 point

I'd expect there to be some test modes built in, so maybe there's a  mode that shows the tile ID and other data on the tile itself. I'm slightly surprised they don't show something at powerup for diagnostics.

  Are you sure? yes | no

SpaceCoaster wrote 11/23/2017 at 02:29 point

I have more rows displayed! Pulsing cpld_p2 does some magic. 

Changes pushed to

Update: setting cpld_p2 to 1 is equally effective.

  Are you sure? yes | no

SpaceCoaster wrote 11/22/2017 at 20:42 point

I transformed the contents of into a simplified NX4 demo program. It can be found on GitHub at

I am most proud of the fact that it compiles without any warnings in ISE!

  Are you sure? yes | no

SpaceCoaster wrote 11/21/2017 at 21:27 point

@modder_mike Could cpld_6 (on m102 test point) be SOUT? When I push data into led_l_sin[1] (m101). I see output on m102 which looks like SOUT. I haven't figured out exactly what SOUT is supposed to look like but my capture contains the input data shifted and vaguely matches the output from a TLC9540/Arduino testbed that I have setup. Is it possible to tell which are CPLD outputs and which are inputs? I am a bit concerned about shorting a pin to ground via the FPGA!

  Are you sure? yes | no

modder_mike wrote 11/22/2017 at 00:05 point

That sounds likely.  The drivers on the LED panel are daisy-chained SOUT to SIN, with the last driver in the string's SOUT connected to the CPLD.  So if you shift data past the last driver, you are shifting it into the CPLD and in turn to wherever the CPLD routes it.  If you see familiar data a few hundred clocks after sending it, that's probably SOUT.

I'll poke around and see if I can come up with some more definitive pin directions.  The only one I can tell you for sure right now is, coincidentally, Pin 6, which is connected to a dedicated input pin on the FPGA.  That, and its location on the FPGA, combined with what you have found, almost certainly points to it being SOUT.

  Are you sure? yes | no

SpaceCoaster wrote 11/22/2017 at 20:24 point

I did some more investigation and m102 is SOUT and as a bonus m116 is XERR.

  Are you sure? yes | no

Mike Harrison wrote 11/20/2017 at 12:24 point

Has anyone checked to see if the FPGA talks to the CPLD at powerup to initialise it? Might give some clues on the protocol.

  Are you sure? yes | no

Ian Hanschen wrote 11/20/2017 at 19:56 point

That's our next step! I was thinking about putting the JTAG for the FPGA in monitor mode, and then attempting a 'reset' (haven't figured out how yet) that doesn't cause the JTAG connection to drop out - and recording the logic level changes of the pins on the FPGA that map to the CPLD. Have a better way to do this?

  Are you sure? yes | no

Mike Harrison wrote 11/20/2017 at 23:19 point

Logic analyzer/scope

  Are you sure? yes | no

Ian Hanschen wrote 11/21/2017 at 02:38 point

Well yeah :) I was fishing for a handy way to do it where I wouldn't need to make an interposer to tap the signals. I guess I could tap solder some thin wire for the analyzer probes.

  Are you sure? yes | no

modder_mike wrote 11/21/2017 at 04:40 point

I've looked at it a bit.  CPLD Pin 8 receives the 40MHz master clock from the FPGA.  CPLD pins 2, 3, 5 and 6 show little blips of data.  Pin 41 goes high corresponding with a break in the incoming master clock, and goes low as the master clock resumes.  Pins 42 and 44 go high briefly then go low and do nothing else for the period I could be bothered to capture.  Pin 43 remains low.  I'll try to capture some data, but I'm working on an ancient HP 16500 mainframe, it's slow going.

  Are you sure? yes | no

SpaceCoaster wrote 11/21/2017 at 19:37 point

cpld_8 must be toggled at more than once per second to produce output. Slower rates only update the top portion of the panel. I think that the CPLD is disabling output after around a second if cpld_8 is not toggled. It could be called a heartbeat or a watchdog.

  Are you sure? yes | no

Ian Hanschen wrote 11/21/2017 at 20:19 point

Whoa! @SpaceCoster do you have the full thing scanning out (all rows)?

  Are you sure? yes | no

SpaceCoaster wrote 11/21/2017 at 20:35 point

Sorry Ian, no extra rows. This is the secret sauce from Richard's code which activates ANY led output. It does eliminate one CPLD pin from the mix of unknown pins.

  Are you sure? yes | no

modder_mike wrote 11/22/2017 at 00:15 point

Hmm.  The CPLD has no local sense of time - it doesn't contain an oscillator, and there is not one on the surrounding board.  There are no RC networks nearby that look suspicious.  So by itself, pin 8 as a watchdog doesn't make sense to me.  The effect may appear watchdog-like, but I think there will be a different answer.  (Don't ask me what that is, I have no idea yet)

  Are you sure? yes | no

Ian Hanschen wrote 11/20/2017 at 16:57 point

At a thousand percent markup. Edit: nevermind

  Are you sure? yes | no

Richard Aplin wrote 11/22/2017 at 06:29 point

That vendor is open to haggling; not as low as our original purchase but within 2-4x e.g. $20 or less per tile. In absolute terms, considering how overengineered they are compared to anything else you can buy, still a deal. I've sent that guy a couple of "make offers" (I lowballed) and he came back in that price range. I have not yet hit 'buy' but I may well... save a 10 pack for me!

  Are you sure? yes | no

Mike Harrison wrote 11/20/2017 at 00:46 point

Another thought on alternative interface - LVDS inputs with a couple of resistors should take RS485, so that would give decent distance. An FTDI FT232H will do up to 12 MBaud, and an FT4232H will give you 4 ports, and can get close to saturating all 4 ports if you get the packet sizing right. That might be a useful alternative, less host-specific interface for smaller arrays.

I did  a teardown workshop on these before Supercon. The thing that puzzled me is why they'd used parallel flash rather than SPI - an internal processor - microblaze or picoblaze might be a plausible reason. I suspect the LED board EEPROM holds per-panel calibration data - would be interesting to compare a few. Not sure what the rear board EEPROM would be for though, apart from serial no. etc.

  Are you sure? yes | no

Richard Aplin wrote 11/21/2017 at 23:08 point

Mm another cheap solution is a cypress FX2 which willl do 20-40mbytes/sec (i.e. saturate usb HS) on 8 or 16 outputs, or even go nuts and use an FX3 which does up to 100mhz x 32bit, which without pulling out a calculator sounds like it'd drive a prettly large video wall. FX2 boards are very cheap ($5) and easy to use (e.g. with 'fx2pipe' under linux.  On a decent USB host (e.g. Orange Pi, intel NUC etc) should be able to run 3 FX2s at the same time running flat out, and HS usb isn't a bad choice for the interconnect if you have the tiles in a regular planar layout and the host in the middle...

1080p60 with RGB888 is 373mbytes/sec which is just about doable on an FX3 hanging off a well tuned USB3 host... I doubt any of us will have the necessary 1800 (60x30) tiles :-)

  Are you sure? yes | no

Mike Harrison wrote 11/20/2017 at 00:33 point

Is the CPLD readable or has it been  secured ?  I wonder if it's a simple  enough device to at least get some clues from its JEDEC file.

Of course another option would simply be to reprogram it, but that would need a JTAG interface, though you'd need that anyway to reprogram the FPGA.

  Are you sure? yes | no

modder_mike wrote 11/21/2017 at 23:44 point

I'd like to try that, but I don't have a proper JTAG adapter to do so.  I don't think the Bus Pirate driver for OpenOCD can do readout (can it?)

The major barrier to reprogramming the CPLD is that you need to disassemble the whole LED panel to get to the JTAG port.  This is supposedly destructive to the plastic shades around the LEDs according to the Barco manual, and drastically increases the complexity of the mod.

That said, most people with these panels only probably have 10 pieces, so I guess the difficulty is kind of moot.  If we were talking thousands of units it'd be a different story.

  Are you sure? yes | no

Mike Harrison wrote 11/20/2017 at 00:29 point

Quick note on dangers of running with no scan - if you set the DC data to 1 on all channels this will reduce the current to what should be a safe level.  I've not looked at the unit in detail, but If I were implementing the CPLD, I'd probably use something like SPI to minimise pins and maintain flexibility for different panel sizes, and maybe also include some timeout protection to prevent permanently turning the row on, to avoid damage.

  Are you sure? yes | no

Richard Aplin wrote 11/20/2017 at 18:27 point

It's my favorite (frustratingly non-prolific) youtuber! Hey there Mike. In the current python we load the dot correct with really low values (e.g. 10 out of a max 63), clearly you can drive it really hard if you want to (don't know what current set resistors they used on the drivers).

 I tried various fuzzing of the cpld pins (it's set up so you can bitbang them from python) but no joy yet; I have got the whole panel to scan a couple of times by accident when it's browned out due to my psu current limit being set low, not very useful but something. Hopefully we'll figure out the cpld soon but if not I may reprogram it when I'm home (lying on beach in Mexico right now, which doesn't suck). 🏖

The NX4 is delightfully overengineered, clearly cost was no concern; I imagine Barco paid TI and Xilinx some fat piles of cash over the years. I particularly like the leds on the back + light sensor, one assumes for monitoring aging and/or thermal response. Deluxe!

  Are you sure? yes | no

modder_mike wrote 11/24/2017 at 20:18 point

On the other curious hand, the control board looks like it was laid out by an intern, with silkscreen that's various sizes, all different orientations and interfering with other silkscreen and component pads...

  Are you sure? yes | no

Richard Aplin wrote 11/15/2017 at 23:49 point

BTW running the pixel clock at 5mhz (current default, could go faster) gives you a total frame update rate of 0.695ms, or  1438fps. This is so fast you could easily add dithering; another 4 bits would have the LSB switching at 1438/16=90hz, which is probably still not at all visible (and you could happily run the pixel clock at 10mhz to double that to 180hz). That would get you an effective 16bit pixel resolution, i.e. 48 bits per pixel. I added that (TEMPORAL_DITHER) in the code but it's not very useful right now unless you also make the frame buffer wider; it's more just to say you can do 48bits per pixel than being useful :-)

  Are you sure? yes | no

Richard Aplin wrote 11/15/2017 at 06:05 point


Status: lots of things work EXCEPT the fucking row scanning. You can hook it to a UART and send RGB framebuffer images and have them displayed and fade them in and out and read/write SRAM and I2C and all sorts of stuff ... but the uncracked nut is getting the CPLD to scan rows. It's probably not very hard it's just a case of poking at it till we find the right thing (or logic analyzing an active tile).  There's another minor thing (likely a floating pin) that needs figuring out; after powerup you currently need to run the "test_pattern_boot" bitstream (that one I posted on here, also provided on github) one time, then the main project works. It's not that it doesn't work it's just there is no display output. Clearly there's a couple of pins (or CPLD state) not figured out yet.

Anyway.. I'm AFK for about 10 days, I hope this is useful/fun and that I put all the necessary files for ISE in github- lmk ASAP if the project doesn't build for you. 

  Are you sure? yes | no

Richard Aplin wrote 11/11/2017 at 05:43 point

I dumped the parallel flash; the Spartan bitstream is right there at the start of it. Elsewhere there's various interesting things, some text strings, some 8b->12b conversion tables, etc.  They might have put a microblaze core in there b/c the plaintext strings don't make much sense otherwise (text in the xlinx bitstream wouldn't show up as ascii in the flash of course).  File (and interesting offset locations) in files area.  Other stuff looking quite promising; I have SRAM, Flash, I2C working from the fpga, plus a working UART host interface talking to some python.  Still no actual pixels lighting up tho; I got wildly distracted with other fun (as you'll see) and left that part broken for ages, but I'll loop back around to that, get it debugged, and post some src and bitstreams

  Are you sure? yes | no

modder_mike wrote 11/11/2017 at 14:43 point

I dumped the first few K of the flash, and I also found it to start with 0xFFFFFFFF5599AA66, so either both of us were off by 4 bytes or that's how it actually begins.

  Are you sure? yes | no

Richard Aplin wrote 11/12/2017 at 22:37 point

yeah I think that's just how it is; I dumped mine twice and both times matched; the xilinx just looks for the bitstream sync marker so it doesn't care too much about the offset.

  Are you sure? yes | no

Shaun wrote 01/04/2018 at 02:34 point

Did you write some code to dump the flash or were you able to get the indirect tools working though Impact? I'm having trouble getting the Xilinx tools to write a non volatile image.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates