Reorder/multiplex bitstreams (FPGA needed?) - Page 1
Forum section: electronics / general

author: stefan_o
author_rank: Regular Contributor
timestamp: May 17, 2017, 06:59:16 pm
content: Hey,I would like to rearrange data to be able to connect two devices, unfortunately the data format is not compatible, I need a device for conversionCode:Input:continuous clockData A (just raw bitsteam)Data B (just raw bitsteam)Code:Output:continuous clock (Input clock x2)Data output as dwords: AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA BBBBBBBB BBBBBBBB BBBBBBBB BBBBBBBB AAAAA...A/B output (low/high): LLLLLLLL LLLLLLLL LLLLLLLL LLLLLLLH HHHHHHHH HHHHHHHH HHHHHHHH HHHHHHHL LLLLL...The maximum input clock is about 7 MHz.What is simplest way two implement that? An µC like AVR is much too slow I assume, what kind of programmable logic device would you recommend? I assume there is no part that does this out of the box.In case you're wondering: I would like to get DSD data from an ADC into an I2S input.Best regardsStefan

author: helius
author_rank: Super Contributor
timestamp: May 17, 2017, 07:53:10 pm
content: You need a PLL to multiply the clock. For the A/B you need 4 32-bit FIFOs. A 74LS224 might work.You could also use small PLDs if you need to minimize space. The Lattice iCE40 should be capable, I think.

author: danadak
author_rank: Super Contributor
timestamp: May 17, 2017, 09:37:31 pm
content: Possibly a PSOC solution -http://www.cypress.com/documentation/application-notes/an82250-psoc-3-psoc-4-and-psoc-5lp-implementing-programmable-logicWritten in Verilog and/or schematic capture solution......Regards, Dana.

author: hamster_nz
author_rank: Super Contributor
timestamp: May 17, 2017, 11:49:06 pm
content: I would think a small CPLD could do it, as long as you have the faster clock available to you - if you don't have the faster clock then you will need a PLL. You will also need to think carefully about how you align the data to avoid slipping bits around (e.g. controlling the CS lines on the source).Are you merging data from two SPI ADCs to an I2S stream?

author: NorthGuy
author_rank: Super Contributor
timestamp: May 18, 2017, 01:01:00 am
content: A small dsPIC33FJ or PIC24HJ can do it.You feed the input clock to the PIC's CLKI pin. It has built-in PLL. You use the PLL to produce 12x system frequency - if input is 7MHz then the system will run at 84 MHz, which means 42 MHz instruction clock.You produce both CLK and H/L with built-in OC or PWM modules, so the CPU needs to only do A/B.The CPU on these chips is 100% predictable - no cache or anything of that sort, so you can bit-bang with 100% surety. The output frequency is 14 MHz, so you will have 3 instructions to produce output, but you don't really need that many.You select your pins to minimize overhead. For example, you can put your A and B inputs at RA0 and RA1. And you put your output at RC0. This way, you can read the byte from PORTA, and your input will contain A at bit 0 and B at bit 1.To output A, you can simply copy the input byte to LATC and the A bit will go to RC0.To output B, you use LSR instruction instead of copying. The instruction shifts the byte by one, so bit 1 will go directly to RC0, thus outputting the B bit.Here's how your logic goes:cycle 1. You read both A and B.Code:mov.b ,[w11++] ; w10 points to PORTA, w11 points to the buffer where you store datacycle 2. You transmit the bit. If you need to transmit A, you doCode:mov.b [w12++], ; w9 points to LATC, w12 points to the memory location where previous data were storedIf you need to transmit B, you doCode:lsr.b [w12++], ; ibidcycles 3-4. These cycles are for housekeepingHere you may adjust w11/w12 so that they wrap around as neededcycle 5. You transmit the second bitcycle 6. Another housekeeping cycleSo, you just repeat these 6 steps 32 times and your data are out. This all takes 32 x 6 = 192 assembler instructions. Then you can jump back, or you can wrap them with DO instruction which does no-overhead looping and it will roll forever.The only thing which requires work is to synchronize the CPU and both PWMs to the input clock, which may require some sort of calibration.

author: stefan_o
author_rank: Regular Contributor
timestamp: May 19, 2017, 12:12:03 pm
content: Thanks for all your great answers, since I never worked with FPGA/CPLDs before and them more complicated than expected, the solution NorthGuy proposed seems to be the best for me.Since I use AVRs rather than PICs, is there an specific feature I have to use an PIC rather than an ATxmega? Only speed is a little lower, with slight overclocking I only get 6 cycles per input.

author: tggzzz
author_rank: Super Contributor
timestamp: May 19, 2017, 02:25:23 pm
content: That bit-bashing couldeasilybe done in software with XMOS processors - and you would be able toguaranteeitbeforeexecuting the code :0Currently I am making a reciprocal frequency counterusing only softwarein a single XMOS processor to capture two 50Mb/s digital streams, count the edges in them, do some arithmetic,andpass the results over USB to a host processor. The latter would stretch the capabilities of most processors, but XMOS handles it easily, by design.The timing isguaranteedby the development environment, so Iknowit can do that continuously without interruption (literally!).The I/O and environment allows I/O to be done on specific clock cycles, with a 2ns resolution. There are many fpga-like I/O facilities including SERDES, handshakes, strobes, serial to parallel conversions etc.For why those remarkable properties are true, seehttps://www.xmos.com/published/xcore-architecture?version=latestThen try it with a £12 development boardhttps://www.digikey.co.uk/en/product-highlight/x/xmos/startkitThe dev environment is free.The code would be something like:in buffered port:32 aIn = XS1_PORT_1F; // the _1F specifies a single input pin, and the buffered port:32 specifies that 32 sequential bits are deserialised into a single wordin buffered port:32 bIn = XS1_PORT_1G;out port dataOut = XS1_PORT_32B; // the 32 bit wide output portout port ab = XS1_PORT_1H; // a 1-bit portconfigure_clock_rate(clkIn, 500, 1); // 500MHz, or could use a clock arriving on an external pinconfigure_in_port(aIn, clkIn, 1);configure_in_port(bIn, clkIn, 1);configure_clock_rate(clkOut, 500, 32); // 1/32 of the clkIn, or could use a clock arriving on an external pinconfigure_out_port(data, clkOut, 1);configure_out_port(ab, clkOut, 1);start_clock(clkIn);start_clock(clkOut);unsigned int aData;unsigned int bData;while (1) {aIn :> aData; // wait until 32 bits available, then continuebIn :> bData; // wait until 32 bits available, then continueab <: 0; // which onedataOut <: aData; // output data one clock later,ab <: 1; // other onedataOut <: bData;}And that's it. But you could also specify that output occurs on clock cycle x with instructions likeab <: 1 @ x;dataOut <: aData @ x;so that both change simultaneously.

author: stefan_o
author_rank: Regular Contributor
timestamp: May 19, 2017, 03:25:35 pm
content: Thanks for your proposal, I do not understand your code example completely, first you fill buffer A complete, then buffer B, does this happen simultaneously?You have the comment "output data one clock later,", how do you handle that one clock later?Does the dev environment contain an emulator? So I can simulate an input signal and the output signal?

author: tggzzz
author_rank: Super Contributor
timestamp: May 19, 2017, 05:45:49 pm
content: There are many processors and a few languages; most are more-or-less interchangeable and all have similar disadvantages w.r.t. hard realtime systems. Learn one and the others are easy; don't bother to learn another until you have to.The XMOS processors and xC language are significantly different and offer very significant advantages w.r.t. parallel processing and hard realtime systems. It is worth learning about them so that you can see the deficiencies in other techniques.Quote from: stefan_o on May 19, 2017, 03:25:35 pmEach port has its own SERDES register (and timer, and more), so bits are clocked in simultaneously as defined by clkIn. In that example, clkIn is 500MHz, but other frequencies can be chosen or it can come from another i/o pin.The core is clocked at 100MHz, so the second bIn :> bData occurs 10ns after aIn :> aData. However, many many other variations are possible, and some may suit your purposes better. Seehttps://www.xmos.com/download/private/Introduction-to-XS1-ports%281.0%29.pdfNote also that these are multicore devices. The Startkit device is 8-core (plus another 8 cores dedicated to the USB interface).It is entirely possible to have a separate core for each i/o "device", with the cores communicating over internal hardware links. For example, below chan means channel, chanend means channel end, and par means execute the functions in parallel, each on a separate corevoid inputA(chanend foo) {while (1) {aPort :> aData; // receiveaData = aData - 1; // no idea why you would want to do that!foo :< aData; // transmit to the process at the other end of the channel}}void inputB(chanend baz) {while (1) {aPort :> aData; // receiveaData = aData - 2; // no idea why you would want to do that!baz :< aData; // transmit to the process at the other end of the channel}}void process(chanend bar1, chanend bar2) {while (1) {bar1 :> a; // receivebar2 :> b; // receivec = a + b; // i.e. c = inputA+inputB-3;}}chan linkA;chan linkB;par {// all these three functions execute simultaneously at 100MHzinputA(linkA);inputB(linkB);process(linkA, linkB);}QuoteDownload xTimeComposer from their website, and see. There is a simulator; I don't know whether it allows simulation of hardware inputs -but that doesn't matter. What you would do is create yet another process for each input, and use a link to connect the simulated output to the receiving processes. You can use the hardware timers to specify exactly which clock an output (or link) changes.If you are familiar with Occam (from the 70s!) and/or Hoare's CSP (Communicating Sequential Processes) then you will feel comfortable with xC. FFI, seehttps://www.xmos.com/download/private/XMOS-Programming-Guide-%28documentation%29%28B%29.pdfN.B. I have no connection with XMOS, but I have wanted to use CSP/Occam for hard realtime systems since the 70s

author: stefan_o
author_rank: Regular Contributor
timestamp: May 19, 2017, 08:08:48 pm
content: I downloaded xTIMEcomposer and copy&paste your code for a start, but it gives me lot's of syntax errors, is it a problem in your code or am I doing something wrong?

author: tggzzz
author_rank: Super Contributor
timestamp: May 19, 2017, 08:22:37 pm
content: Quote from: stefan_o on May 19, 2017, 08:08:48 pmI haven't tried that code; it is pseudocode illustrating the language and hardware concepts and facilities. Nonetheless, you can use it as a close guide to what you might need to do.I suggest you take one of their examples, and mutate it in the direction I suggested.

author: stefan_o
author_rank: Regular Contributor
timestamp: May 19, 2017, 10:54:10 pm
content: Quote from: tggzzz on May 19, 2017, 08:22:37 pmAh ok, since I do neither know the language nor the specific instructions for the XMOS, I could not see that. I looked at several examples but I can't find anything that really explains how this asynchronous inputs/outputs work.Is there any tutorial you can recommend for somebody how knows C but not XC that is more detailed on what exactly the instructions do compared to the comments in their examples?Edit: I was able to modify/complete the source so it compiles. But I couldn't find out how to simulate it:Code:#include <xs1.h>#include <platform.h>in buffered port:32 aIn = XS1_PORT_1F; // the _1F specifies a single input pin, and the buffered port:32 specifies that 32 sequential bits are deserialised into a single wordin buffered port:32 bIn = XS1_PORT_1G;out port dataOut = XS1_PORT_32B; // the 32 bit wide output portout port ab = XS1_PORT_1H; // a 1-bit portclock clkIn = XS1_CLKBLK_1;clock clkOut = XS1_CLKBLK_2;int main(void) {configure_clock_rate(clkIn, 500, 1); // 500MHz, or could use a clock arriving on an external pinconfigure_in_port(aIn, clkIn);configure_in_port(bIn, clkIn);configure_clock_rate(clkOut, 500, 32); // 1/32 of the clkIn, or could use a clock arriving on an external pinconfigure_out_port(dataOut, clkOut, 0);configure_out_port(ab, clkOut, 0);start_clock(clkIn);start_clock(clkOut);unsigned int aData;unsigned int bData;while (1) {aIn :> aData; // wait until 32 bits available, then continuebIn :> bData; // wait until 32 bits available, then continueab <: 0; // which onedataOut <: aData; // output data one clock later,ab <: 1; // other onedataOut <: bData;}}What I still don't get, what are blocking operations, what are non blocking? At the start you comment "wait until 32 bits available, then continue", does it wait or does it start to sample? "output data one clock later," Why is it one clock later? Does the <: operator copies the argument in some output buffer? aData and bData should be changing while outputting aData/bData

author: stefan_o
author_rank: Regular Contributor
timestamp: May 20, 2017, 08:57:37 pm
content: Code compiles and does not crash the simulator:Code:#include <xs1.h>#include <platform.h>in port inClock = XS1_PORT_1E;in buffered port:32 aIn = XS1_PORT_1F; // the _1F specifies a single input pin, and the buffered port:32 specifies that 32 sequential bits are deserialised into a single wordin buffered port:32 bIn = XS1_PORT_1G;out port outClock = XS1_PORT_1C; // a 1-bit portout port dataOut = XS1_PORT_32B; // the 32 bit wide output portout port ab = XS1_PORT_1H; // a 1-bit portclock clkIn = XS1_CLKBLK_1;clock clkOut = XS1_CLKBLK_2;int main(void) {//configure_clock_rate(clkIn, 500, 1); // 500MHz, or could use a clock arriving on an external pinconfigure_clock_src(clkIn, inClock);configure_in_port(aIn, clkIn);configure_in_port(bIn, clkIn);//configure_clock_rate(clkOut, 500, 32); // 1/32 of the clkIn, or could use a clock arriving on an external pinconfigure_port_clock_output(outClock, clkOut);configure_out_port(dataOut, clkOut, 0);configure_out_port(ab, clkOut, 0);start_clock(clkIn);start_clock(clkOut);unsigned int aData;unsigned int bData;while (1) {aIn :> aData; // wait until 32 bits available, then continuebIn :> bData; // wait until 32 bits available, then continueab <: 0; // which onedataOut <: aData; // output data one clock later,ab <: 1; // other onedataOut <: bData;}return 0;}Beside the questions from previous post, what I could not find out, how do I multiply the clock? Output clock has to be twice of input clock. I found some information about PLL of system clock, do I need to synchronize the system clock with the input clock or is there another way?

author: tggzzz
author_rank: Super Contributor
timestamp: May 20, 2017, 09:48:31 pm
content: Ports: see Ports reference in reply #8xC tutorial: see Programming Guide reference in reply #8Simulation: probably inhttps://www.xmos.com/download/private/xTIMEcomposer-User-Guide-14%2814.x%29.pdfClocks: see first reference inhttps://www.google.co.uk/search?q=xmos+clocks

author: stefan_o
author_rank: Regular Contributor
timestamp: May 21, 2017, 09:51:06 pm
content: Sorry for all my questions, I never worked with XMOS before, I know AVR and for AVR there are hundreds of tutorials, examples and snippets for everything (even without including Arduino), compared to that there is basically nothing about XMOS.I read those document about clocks before, I'm not sure, but I think there is a problem: The maximum input frequency is 6.144MHz, but the minimum is below 4MHz, I found another document that says minimum input frequency for PLL of the XMOS is 4.22 MHz.Is there another option to double a frequency other than PLL? Or do I need external hardware and feed it into the device?I modified the code and I am quite confident it should work, did not find a way to simulate input, but output seems to work.Code:#include <xs1.h>#include <platform.h>in port inClock = XS1_PORT_1E;in buffered port:32 lIn = XS1_PORT_1F; // the _1F specifies a single input pin, and the buffered port:32 specifies that 32 sequential bits are deserialised into a single wordin buffered port:32 rIn = XS1_PORT_1G;out port outClock = XS1_PORT_1C; // a 1-bit portout buffered port:32 dataOut = XS1_PORT_1B; // the 32 bit wide output portout buffered port:32 lrClock = XS1_PORT_1H; // a 1-bit portclock clkIn = XS1_CLKBLK_1;clock clkOut = XS1_CLKBLK_2;int main(void) {configure_clock_src(clkIn, inClock);configure_in_port(lIn, clkIn);configure_in_port(rIn, clkIn);//configure_clock_rate(clkOut, 100, 10); // 1/32 of the clkIn, or could use a clock arriving on an external pinconfigure_port_clock_output(outClock, clkOut);configure_out_port(dataOut, clkOut, 0);configure_out_port(lrClock, clkOut, 0);start_clock(clkIn);start_clock(clkOut);unsigned int in_l_a=0;unsigned int in_r_a=0;unsigned int in_l_b=0;unsigned int in_r_b=0;const unsigned int high=0xfffffffe;const unsigned int low=0x1;while (1) {par {lIn :> in_l_a;rIn :> in_r_a;{par { lrClock <: high;dataOut <: in_l_b; }par { lrClock <: low;dataOut <: in_r_b; }}}par {lIn :> in_l_b;rIn :> in_r_b;{par { lrClock <: high;dataOut <: in_l_a; }par { lrClock <: low;dataOut <: in_r_a; }}}}return 0;}

author: tggzzz
author_rank: Super Contributor
timestamp: May 21, 2017, 10:51:51 pm
content: I suggest you ask about the clocks on the XMOS forums.Don't forget that you can specify that an input occurs on a specific clock cycle (a timed input) where the clock can be up to, I believe, 500MHz. You can use either common timers or the 16 bit counter in each up port.I would have a separate process generating the simulated input, and connect that simulated input to your process via a channel.You should verify whether your par statements will execute on one core or on different cores, and work out what is appropriate for you.

author: tggzzz
author_rank: Super Contributor
timestamp: May 22, 2017, 06:47:18 am
content: It isn't clear to me how you know when to sample the external inputs, with xC or Arduino or anything else.One mentality that you need to change with xC and the XMOS devices is that it is perfectly acceptable and normal to have one corecompletely halted, waiting for an input to come along. The other cores continue merrily, e.g. doing output.Hence, if there is an external strobe/clock that doesn't fit the standard I/O port hardware, then can have an void inputProcess() {...} something likewhile (1)// elegant way of waiting for an input condition or a timeoutselect {case inputClock when pinseq(1) :> dummy:// read data input// pass data through a channel to the output processbreak;case check on a timer for timeout// ...break}// or ugly spinloop waiting for another conditiondo {inputClock :> x;} while (input != 0);}

author: hamster_nz
author_rank: Super Contributor
timestamp: May 22, 2017, 11:01:45 am
content: If you were still open to an FPGA/CPLD solution, here's one that seems to work along the desired way, using a DDR output to avoid the need for a PLL(see simulation below)Code:library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.NUMERIC_STD.ALL;Library UNISIM;use UNISIM.vcomponents.all;entity merge isPort ( clk : in STD_LOGIC;in_a : in STD_LOGIC;in_b : in STD_LOGIC;out_lr : out STD_LOGIC;out_d : out STD_LOGIC);end merge;architecture Behavioral of merge issignal in_sr_a : std_logic_vector(15 downto 0) := (others => '0'); -- input shift registersignal in_sr_b : std_logic_vector(15 downto 0) := (others => '0'); -- input shift registersignal out_sr_d : std_logic_vector(31 downto 0) := (others => '0'); -- output shift registersignal out_sr_lr : std_logic_vector(31 downto 0) := (others => '0'); -- output shift register for LR clksignal count : unsigned(3 downto 0) := (others => '0');beginODDR_lr : ODDRgeneric map(DDR_CLK_EDGE => "SAME_EDGE", INIT => '0', SRTYPE => "SYNC")port map (Q => out_lr, C => clk, CE => '1',D1 => out_sr_lr(0),D2 => out_sr_lr(1),R => '0', S => '0');ODDR_d : ODDRgeneric map(DDR_CLK_EDGE => "SAME_EDGE", INIT => '0', SRTYPE => "SYNC")port map (Q => out_d, C => clk, CE => '1',D1 => out_sr_d(0),D2 => out_sr_d(1),R => '0', S => '0');process(clk)beginif rising_edge(clk) thenif count = 15 thenout_sr_d <= in_sr_a & in_sr_b;out_sr_lr <= x"80007FFF";count <= (others => '0');elseout_sr_d <= "00" & out_sr_d(31 downto 2);out_sr_lr <= out_sr_lr(1 downto 0) & out_sr_lr(31 downto 2);count <= count+1;end if;in_sr_a <= in_a & in_sr_a(15 downto 1);in_sr_b <= in_b & in_sr_b(15 downto 1);end if;end process;end Behavioral;

author: agehall
author_rank: Frequent Contributor
timestamp: May 22, 2017, 01:38:33 pm
content: OT: I'd never heard of XMOS processors before but after reading up about them, they seem freaking awesome! I have to get me some of that magic sauce me thinks! Thanks for putting me onto those little gems.

author: tggzzz
author_rank: Super Contributor
timestamp: May 22, 2017, 02:44:38 pm
content: Quote from: agehall on May 22, 2017, 01:38:33 pmAll tools and technologies have "rough edges", but these remove many rough edges in traditional tools.I'm still learning about the XMOS toolchain; but I like its pedigree and so far I haven't found anyfundamentalproblems.

author: NorthGuy
author_rank: Super Contributor
timestamp: May 22, 2017, 04:32:34 pm
content: Quote from: hamster_nz on May 22, 2017, 11:01:45 amI agree. If you want something new and interesting to learn, go with real FPGA and VHDL/Verilog. These are completely different from C/asm (or anything you can find on MCU) and they are extremely powerful and flexible.

author: tggzzz
author_rank: Super Contributor
timestamp: May 22, 2017, 04:46:36 pm
content: Quote from: NorthGuy on May 22, 2017, 04:32:34 pmThey are indeed.They also have a significant learning curve, related to the "hardware mentality", the language itself, and the complexity of toolchains. The first two are inevitable; the last is irritating and has to be dealt with.Sometimes only one technology will solve the problem; more often several technologies could solve the problem. Knowing multiple technologies allows selection of the most appropriate for the task, so that you avoid using a hammer to insert nails.

author: helius
author_rank: Super Contributor
timestamp: May 22, 2017, 05:24:15 pm
content: Quote from: tggzzz on May 22, 2017, 04:46:36 pmI think everybody knows what you're getting at, but avoiding using a hammer to insert a nail is a good metaphor for how perverse many hobbyist projects can be.

author: tggzzz
author_rank: Super Contributor
timestamp: May 22, 2017, 05:41:30 pm
content: Quote from: helius on May 22, 2017, 05:24:15 pmAaargh! Doh!I agree, but there can be good reasons for using a new technology on a simple "hobbyist" project: to see whether it lives up to its promise, or just to see how far things can be pushed before they break.Two examples from my recent experience are using a cheap SDR dongle as a network analyser and TDR, and making anFin/Freffrequency counter using software alone. The former can resolve impedance discontinuities ~3cm apart, the latter works with a 12.5MHz frequency inputs (and possibly 25MHz).

author: NorthGuy
author_rank: Super Contributor
timestamp: May 22, 2017, 05:53:07 pm
content: Quote from: tggzzz on May 22, 2017, 04:46:36 pmYes, tools are horrible. I can only speak of Xilinx. They had ISE. Now they came up with new tool, called Vivado. It is very big (20GB download), slow, and not very well thought out. But if you manage to get through it, it is worth it.The FPGA mentality is indeed completely different. If learning is a part of your goal, it is a fascinating thing to try. It will broaden your perspective and will give you a different view. Of course, it costs more, but ...

author: stefan_o
author_rank: Regular Contributor
timestamp: June 11, 2017, 10:39:21 pm
content: Quote from: hamster_nz on May 22, 2017, 11:01:45 amThat looks really interesting, but at the moment this seems a bit too complex for me. But I will definitely keep that in mind, especially the trick of using DDR outputs. In fact this problem is more complex than I originally thought, it would add to much cost and effort to the entire project.Thank you all for your help!