LFSR - Page 1
Forum section: electronics / general

author: NivagSwerdna
author_rank: Super Contributor
timestamp: May 17, 2022, 10:51:46 am
content: I'm about to embark on my first FPGA project and have decided on verilog...So one of the first things I have to build is a LFSR where various taps combine to feedback...This got me thinking...If the LSFR is represented by a series of flip flops each feeding each other and sharing a common clock with the input either the output of the previous gate or a combinatorial function of various prior taps...Is this inevitably a race condition because the inputs are based on the outputs which change at the same clock edge?I have seen code like...out <= { out[6:0], feedback }Does that guarantee that it works atomically even when feedback is derived from the values of out on the RHS?Maybe I am overthinking this?Thanks in advance

author: AndyC_772
author_rank: Super Contributor
timestamp: May 17, 2022, 11:07:13 am
content: No, it's OK - this is why vendors' synthesis tools are used.I speak VHDL, not Verilog, but the basic premise is the same - in synchronous logic, any reference to the state of a signal means the state of that signal at the instant just before the active clock edge.The synthesis tool contains a timing model of the FPGA, which the manufacturer guarantees across voltage, temperature and part-to-part variation. Provided the tool's timing analyser indicates positive setup & hold slack, you compile your code for the correct variant of the device, and you use that device within the manufacturer's recommended spec, then your design will meet timing.In more complex designs - especially those with multiple clocks - then you do need to very carefully consider how signals cross clock domains, and you must create a timing constraints file (.SDC) which describes the relationships between your clocks and other important signals. Some people make a career out of this.Incidentally, it's also why open source FPGA tools aren't really a thing. There have been a few attempts, but in the absence of a guaranteed (proprietary) timing model for the device, there's no way to ensure that setup times are met at any particular clock frequency, or that hold times are met at all.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: May 17, 2022, 11:33:36 am
content: That's interesting. So there is a contract in there somewhere where the LHS relies on the RHS being fixed at some time and the subsequent assignment propagating to the LHS.I don't know how a FF with feedback actually guarantees this behaviour but it must do otherwise shift registers wouldn't work!In my example above the feedback is derived by a continuous net assignment ..E.g.feedback = out ^ outWhich gives an interesting mix of combinatorial and sequential in the assignment of the register.This is obviously a trivial case but it is interesting that you mention the implicit rules being applied to the synthesis... I guess I have a lot to learn.Thanks

author: AndyC_772
author_rank: Super Contributor
timestamp: May 17, 2022, 11:56:41 am
content: There's really two parts to this.Firstly, you need to be able to write code that has a clear, well defined, guaranteed meaning, and that's down to the language itself.Second, this has to be physically implemented in a way that behaves as per your intent. This is down to causality; a signal cannot, in practice, change until some time after a clock edge has been received. Every logic gate, and the connections between its output and any inputs to other gates (including itself), takes time to switch state because of parasitic capacitance. This, helpfully, guarantees that the input to a gate won't change until *after* a clock has been received, provided the clock itself isn't delayed.FPGAs contain global clock nets, hard wired into the silicon, for this very reason. It helps ensure that every logic cell gets a clock at more or less the same time, and the data inputs change later - by which time these inputs have already been latched.

author: langwadt
author_rank: Super Contributor
timestamp: May 17, 2022, 12:45:42 pm
content: Quote from: NivagSwerdna on May 17, 2022, 11:33:36 amthe simplest explanation is that nothing can happen instantly, so an FF output cannot change until some time after the clock edge, reacting on what the input was right before the clock edge

author: emece67
author_rank: Frequent Contributor
timestamp: May 17, 2022, 02:45:29 pm
content: .

author: Bassman59
author_rank: Super Contributor
timestamp: May 17, 2022, 03:05:26 pm
content: Quote from: NivagSwerdna on May 17, 2022, 11:33:36 amYou need to understand how synchronous logic is described in Verilog. You will use synchronous logic to design this LFSR. Let's code up a simple (standard) shift register, which shifts left on each tick of the clock.Code:reg [7:0] sr;reg inbit;always @(posedge clk or negedge rst_l) begin // 1if (~rst_l) begin // 2sr <= 0; // 3endelse begin // 4sr<= { sr[6:0], inbit }; // 5endendLet's look at this in detail.Line 1 is the start of the block and includes the sensitivity list. The sensitivity list details all signals that trigger the block. Changes on any other signals are ignored. In this case, our block is sensitive to the rising edge of the clock and the falling edge of an asynchronous reset.Line 2: test for the assertion of the async reset. How do we know it's an asynchronous reset? It doesn't care about the clock. And we wrote it first in our block, so it has precedence over the clock. If the reset is low, then the shift register is cleared and the block suspends until the next event on the sensitivity list. Do note the use of the<=non-blocking assignment operator. it's not the same as the standard blocking assignment=and "non-blocking" means that the assignment is scheduled and could be overwritten if there is a later assignment to the same signal in the block.The last assignment wins.It is vital that you understand the difference. "Blocking" means that the left-hand-side signal takes on the new valueimmediatelyand in real hardware with a flip-flop, that isnotwhat happens.Also note that in a synchronous system, the clock is always running. You can -- and likely will -- get a rising edge of clk when the reset is asserted. The code above says, "OK, fine, I saw a rising edge on the clock. But reset is active, so I will clear the shift register and basically ignore the clock." This is exactly the behavior you want.Line 3 is where we clear the shift register.Line 4: "else." Since the block is triggered by only two events -- rising clock, falling reset -- and we've already tested for the assertion of reset, then the only possible reason the block was triggered is because we saw the clock rising edge.Line 5: on the clock rising edge: look at thecurrentstate of the signalssrandinbit. What are their values at the instant the clock edge occurred? Take those values and use them to schedule an assignment tosr. And when you've finished evaluatingallright-hand sides of all statements in your block,sris updated and now has the new value you assigned.Since our block is sensitive only to the clock and reset, the change in the value ofsrdoes not trigger the block and all is well.Really, all of the above is fundamental Verilog and youmustunderstand it if you are to be successful.(Also, VHDL works the same way, with the exception that all assignments are non-blocking unless you specifically use variables, and variables don't exist outside of the process in which they are declared.)Hope this helps.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: May 17, 2022, 06:05:56 pm
content: Thanks all. I'm teaching myself this so it isn't always obvious what I don't know!The various points made above have been very helpful!Specifically I hadn't appreciated the significance of tSetup and tHold but now I appreciate those I can also appreciate the issue with clock skew in the context of synchronous logic! That all makes sense now.I also spent some quality time with the IEEE Verilog Standard and this does describe the semantics that I had anticipated... i.e in temporal terms the RHS is evaluated prior to assignment to the LHS so that seems to fit my mental model.There is an example here...https://www.nandland.com/vhdl/modules/lfsr-linear-feedback-shift-register.htmlI'm quite intrigued by... (Assuming 8 bits)...Code:always @(posedge i_Clk)begin...r_LFSR <= {r_LFSR[8-1:1], r_XNOR};...endandCode:always @(*)beginr_XNOR = r_LFSR ^~ r_LFSR ^~ r_LFSR ^~ r_LFSR;end // always @ (*)My remaining questions:In the first always block the use of = and <= would produce the same result?The feedback value is separated into a different block, couldn't it be evaluated in the same block or would that halve the cycle rate?

author: BrianHG
author_rank: Super Contributor
timestamp: May 17, 2022, 06:24:05 pm
content: Here is a 43 bit LFSR & 37 bit CASR examples, synchronous logic with XOR feedback at selected bits. You can easily regenerate an 8bit / or any other size from this example:always @(posedge clk) begin...CASR[36:0] <= ({CASR[35:0],CASR}^{CASR,CASR[36:1]} ^ CASR<<27) ;LFSR[42:0] <= ({LFSR[41:0],LFSR}^LFSR<<41 ^ LFSR<<20 ^ LFSR<<1) ;out [31:0] <= ( LFSR [31:0] ^ CASR[31:0] );The parts in red contain the circulating buffer. The parts in green select which bits are xored at which bit position when injected back into the oscillator. Remember, you need a power-up or reset set point which does not equal '0'. Note that to improve random coverage, even though my final 'out' is only a 32 bit number, I do require a larger width LFSR than 32 bits to achieve all possible numbers. Example, with a 8 bit LFSR, do not expect every possible 8 bit number out as a result. You probably need a well tuned 13 bit LFSR to get every possible 8bit result out of it. Mixing 2 types of random number generators at different widths does improve the fine spectrum granularity of the white noise output but LFSR all by itself is fine for many applications.Full code:Code:module rnd ( clk, rst, ena, load, seed, out );input clk, rst, ena, load;input [31:0] seed ;output reg [31:0] out ;reg[36:0] CASR;reg[42:0] LFSR;always @(posedge clk) beginif ( rst ) beginCASR <= (37'h100000000); // Random starting point.LFSR <= (43'h100000000); // Random starting point.end else if (load) beginCASR <= 37'(seed) | 33'h100000000 ; // Load seed, protect from a seed of 0.LFSR <= 43'(seed) | 33'h100000000 ; // Load seed, protect from a seed of 0.end else if (ena) beginCASR[36:0] <= ( {CASR[35:0],CASR} ^ {CASR,CASR[36:1]} ^ CASR<<27 ) ;LFSR[42:0] <= ( {LFSR[41:0],LFSR} ^ LFSR<<41 ^ LFSR<<20 ^ LFSR<<1 ) ;out [31:0] <= ( LFSR [31:0] ^ CASR[31:0] );endendendmodule

author: NivagSwerdna
author_rank: Super Contributor
timestamp: May 17, 2022, 06:49:01 pm
content: So @BrianHG this is a great example...Taking just the CASR...Does it tick once per clock? i.e. is the expression within the RHSCASR} ^ {CASRCASR[36:1]} ^ CASR<<27concatenationassignmentAll done in one cycle?(I guess that is a slightly loaded question since it probably depends on the propagation time evaluating the expressions to fit within cycle time)PSI did say I was over thinking this!

author: BrianHG
author_rank: Super Contributor
timestamp: May 17, 2022, 06:53:07 pm
content: Also, google LFSR and Xilinx. They have a white paper with the optimum xor mixing bit positions for LFSR width from 5 bit through at least 16 bits if not 24 from memory. You would just edit my above code to their table of optimum XOR points for each width. The also cover the topic of output coverage and you will notice that you will see it is never 100%, otherwise a seed of '0' should run the random number generator, yet it wont.

author: langwadt
author_rank: Super Contributor
timestamp: May 17, 2022, 07:01:36 pm
content: Quote from: BrianHG on May 17, 2022, 06:53:07 pmxapp052 list the taps for LFSRs with 3 to 168 bits

author: BrianHG
author_rank: Super Contributor
timestamp: May 17, 2022, 07:03:55 pm
content: In verilog programming, everything on the right side of the ' <= ' is instantly evaluated all as instant logic gates and wires, including the concatenation. The reg on the left side of the ' <= ' is a block of D-flipflops clocked by the always @(posedge clk) since it is contained within that always. Everything happens in parallel.It is the FPGA compiler's job to worry about every D- input of the reg on the left side being ready in time by the next clock which is where you eventually end up running into an FMAX bottle neck with huge formulas with huge bits on the right side.Note that in my example which uses 2 random number generators in parallel, because my 'out' has its own ' <= ', my out result is delayed 1 additional clock compared to the LFSR and CASR generators. If you do not want this delay, my code would have looked like this:Code:module rnd ( clk, rst, ena, load, seed, out );input clk, rst, ena, load;input [31:0] seed ;output wire [31:0] out ;reg[36:0] CASR;reg[42:0] LFSR;assign out [31:0] = ( LFSR [31:0] ^ CASR[31:0] );always @(posedge clk) beginif ( rst ) beginCASR <= (37'h100000000); // Random starting point.LFSR <= (43'h100000000); // Random starting point.end else if (load) beginCASR <= 37'(seed) | 33'h100000000 ; // Load seed, protect from a seed of 0.LFSR <= 43'(seed) | 33'h100000000 ; // Load seed, protect from a seed of 0.end else if (ena) beginCASR[36:0] <= ( {CASR[35:0],CASR} ^ {CASR,CASR[36:1]} ^ CASR<<27 ) ;LFSR[42:0] <= ( {LFSR[41:0],LFSR} ^ LFSR<<41 ^ LFSR<<20 ^ LFSR<<1 ) ;endendendmoduleThis version of my code returns the result 1 clock sooner at the expense of a slightly lower potential FMAX when wired to the next module in the system. This is because the ' assign out = ' is not a register and clocked, it is immediate hard wired async XOR gates. However, this is only an issue when you want to run this random number generator above 250MHz on a slow FPGA.

author: Bassman59
author_rank: Super Contributor
timestamp: May 17, 2022, 07:54:29 pm
content: Quote from: NivagSwerdna on May 17, 2022, 06:05:56 pmFor purposes of functional and behavioral modeling, setup and hold times actually don't matter. The FPGA timing analysis takes them into account when determining whether your design meets your clock period constraint. Inside the FPGA, you actually have no idea what the set-up and hold time need to be, but you don't care. The tools manage all of it.(Of course it's different when interface the FPGA to some external peripheral logic. That's not what we are concerned with here.)QuoteYes, exactly, but rememberwhenthey are evaluated. Also understand that conditionals (if statements, for example) are considered to be on the right-hand-side of any assignment.QuoteWell, the differences between the two are quite obvious from their sensitivity lists: the former is a synchronous process, triggered only with a rising edge on the clock, and the latter is combinatorial. The sensitivity list shortcut*means "every signal on the right-hand side of the assignment in this block." So the block is triggered when any of the four signals on your RHS change.And do note the different assignment operators! In the synchronous block we use<=and in the combinatorial block we use=. Why? Because we want the combinatorial result to update immediately after evaluation.(This simple example really doesn't make it clear why, but when you get into more complex synchronous processes you will appreciate the difference.)QuoteThis is a "gotcha" question. The answer is: yes. But only because the design is so simple. When you have a lot of always blocks in parallel, all synchronous to the clock, the idea is that all left-hand sides update simultaneously. The non-blocking assignment guarantees that. The blocking assignment does not.There is aclassic paperon this topic. Read it. Grok it. I cannot stress enough how important it is to understand this.QuoteI would roll the feedback calculation into the same synchronous block that calculates the result. I don't see the point of separating them.In reality, what will happen is that the synchronous block will, after a rising edge, give you a new r_LFSR, and immediately after the combinatorial block gives you a new r_XNOR. But! The synchronous block won't look at that new r_XNOR until the next clock edge.You should simulate this to convince yourself that it's true.

author: langwadt
author_rank: Super Contributor
timestamp: May 17, 2022, 08:38:45 pm
content: Quote from: Bassman59 on May 17, 2022, 07:54:29 pmfor simulation, something like:r_LFSR <= #1 {r_LFSR[8-1:1], r_XNOR};will help illustrate that r_LFSR doesn't change until after the clock edge

author: NivagSwerdna
author_rank: Super Contributor
timestamp: May 17, 2022, 10:37:31 pm
content: It seems to work... thanks for the help people...Code:module lsfr (input i_clk,input i_reset,input i_sample,output [15:0] o_lfsr_data,output [31:0] o_lfsr_counter);reg [15:0] r_lfsr = 0;reg [31:0] r_counter = 0;reg r_feedback;always @(posedge i_clk)beginif (i_reset == 1)beginr_lfsr = 0;r_counter = 0;endelsebeginr_lfsr <= {r_lfsr[14:0], r_feedback};r_counter <= r_counter + 1;endendalways @(*)beginr_feedback = (( r_lfsr + r_lfsr + r_lfsr + r_lfsr) % 2) ^ i_sample;endassign o_lfsr_data = r_lfsr;assign o_lfsr_counter = r_counter;endmodule

author: BrianHG
author_rank: Super Contributor
timestamp: May 17, 2022, 11:21:16 pm
content: Why didn't you:Code:module lsfr (input i_clk,input i_reset,input i_sample,output [15:0] o_lfsr_data,output [31:0] o_lfsr_counter);reg [15:0] r_lfsr = 0;reg [31:0] r_counter = 0;// *** Option 1 ***wire r_feedback ;assign r_feedback = (( r_lfsr + r_lfsr + r_lfsr + r_lfsr) % 2) ^ i_sample;// *** Option 2 ***wire r_feedback = (( r_lfsr + r_lfsr + r_lfsr + r_lfsr) % 2) ^ i_sample;// *** Option 3 ***wire r_feedback = (1)'( r_lfsr + r_lfsr + r_lfsr + r_lfsr) ^ i_sample;// *** Option 4 ***wire r_feedback = r_lfsr ^ r_lfsr ^ r_lfsr ^ r_lfsr ^ i_sample;always @(posedge i_clk)beginif (i_reset == 1)beginr_lfsr = 0;r_counter = 0;endelsebeginr_lfsr <= {r_lfsr[14:0], r_feedback};r_counter <= r_counter + 1;endendassign o_lfsr_data = r_lfsr;assign o_lfsr_counter = r_counter;endmoduleNote that your code only shifts in a random bit and slides everything to the left every clock. You have no flipping bits in the middle.Also note that when you reset your LFSR, you make it 0, so unless your input is 1 when clocking, the output will remain 0.

author: hamster_nz
author_rank: Super Contributor
timestamp: May 18, 2022, 12:17:50 am
content: One really neat project to play with LFSRs is making a self-synchronizing scrambler and descrambler. They are used to convert data into what looks to be pseudo-random bits, and then make the original data reappear from the 'noise' again! It really does seem like magic...The source data stream is combined with an LFSR, and transmitted. At the receiving end the 'scrambled' bits are put into a second LFSR, and the original bits pop out again!https://en.wikipedia.org/wiki/ScramblerThey are pretty much used wherever high speed serial comms is used (10GbE, DisplayPort, PCI, SDI, SATA, later HDMI standards, 64B66B coding...). It is used because it ensures that even if the data stream is all 1s or 0s the scrambled version has lots of bit transitions. This allows the receiver's clock recovery to work.It is the sort of project where the learnings will pop up over and over again in different domains.10/10 - highly recommended.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: May 18, 2022, 07:39:47 am
content: Code:// *** Option 1 ***wire r_feedback ;assign r_feedback = (( r_lfsr + r_lfsr + r_lfsr + r_lfsr) % 2) ^ i_sample;// *** Option 2 ***wire r_feedback = (( r_lfsr + r_lfsr + r_lfsr + r_lfsr) % 2) ^ i_sample;// *** Option 3 ***wire r_feedback = (1)'( r_lfsr + r_lfsr + r_lfsr + r_lfsr) ^ i_sample;// *** Option 4 ***wire r_feedback = r_lfsr ^ r_lfsr ^ r_lfsr ^ r_lfsr ^ i_sample;Option 1 & 2 appear identical?Option 3 is intereresting, I wonder if that differsOption 4... I'm not sure about that... I'm not sure it is equivalent.

author: langwadt
author_rank: Super Contributor
timestamp: May 18, 2022, 08:16:08 am
content: Quote from: hamster_nz on May 18, 2022, 12:17:50 amand alsohttps://en.wikipedia.org/wiki/Direct-sequence_spread_spectrum

author: tggzzz
author_rank: Super Contributor
timestamp: May 18, 2022, 08:44:58 am
content: FSMs arethekey starting point for digital logic design.Just as with computer programming languages, there are "design patterns" for hardware programming languages.One of the most useful ones for FSMs is the "two process" pattern, where you conceptually and logically separate the (asynchronous) combinatorial logic from the (synchronous) storage - and encode them separately.The thinking is along the lines of:combinatorial logic encodes the "given the current state and the inputs", the next state should be". That's where the interesting design and implementation occurs, and can be arbitrarily complex - and slowthe flip flop logic is "when the clock occurs the current state becomes the next state". That's trivial to code!You will be aware that HDLs have different syntax and constructs for the asynchronous and synchronous parts.Oddly enough that pattern is also very useful in real-time software systems, but few softies are taught it(BTW, are you going to the Dunstable Downs hamfest on Sunday?)

author: Bassman59
author_rank: Super Contributor
timestamp: May 18, 2022, 07:16:04 pm
content: Quote from: tggzzz on May 18, 2022, 08:44:58 amAgain with the two-process latch-generator.

author: BrianHG
author_rank: Super Contributor
timestamp: May 18, 2022, 07:45:13 pm
content: Quote from: NivagSwerdna on May 18, 2022, 07:39:47 amOk, fine, but there was a larger point I was making.Your :Code:reg r_feedback;...always @(*)beginr_feedback = (( r_lfsr + r_lfsr + r_lfsr + r_lfsr) % 2) ^ i_sample;endYou requested that an un-clocked register / logic cell 'r_feedback' buffer was to be generated. Now, most modern compilers like Altera and Xilinx will see this and automatically remove the redundant logic cell and convert the r_feedback into a wire changing what you wrote :r_lfsr <= {r_lfsr[14:0], r_feedback};into this :r_lfsr <= {r_lfsr[14:0], ((( r_lfsr + r_lfsr + r_lfsr + r_lfsr) % 2) ^ i_sample) };However, if you were to use a dumber FPGA compiler or, say within Altera, in the compile options, set the feature 'remove redundant logic cells' to 'OFF', compiling your design will generate a different result. The compiler will create an UN-clocked logic cell whose inputs are connected to a structure of gates equaling what you write inside your 'always @(*)', and that 'r_feedback' logic cell's output will feed the your 'r_lfsr <= ' adding FPGA routing, and slowing down your maximum possible FMAX. My 'wire r_feedback =' will not suggest to the compiler that you wish to deliberately slow down and add the extra logic cell and routing involved in your design.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: May 19, 2022, 04:51:07 pm
content: Ah. Yes. I meant wire for feedback. That was not intentional!The part about being optimised away is especially interesting.