-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
memory_libmap support for K6N10f #1
Comments
@nakengelhardt let's use this issue for further discussion on memory_libmap support |
I had a look at the examples in https://github.com/QuickLogic-Corp/yosys-f4pga-plugins/tree/main/ql-qlf-plugin/tests/qlf_k6n10f, especially bram_tdp and bram_tdp_split, and I am confused by your definition of TDP mode. It seems that the input in bram_tdp.v describes a memory with two independent read ports and two independent write ports, so four ports total. But the output of the current |
The basic TDP36K BRAM IP has only one addr port (no separate read address and write address): In the mapping the read address & write address are multiplexed to port address based on read or write: Yes, you are correct it would write to the read address when rce and wce are enabled simultaneously of the same port (A or B). |
In that case, we won't be able to reuse the existing
Which ones need to always be equal and which are allowed to be set to different values? |
With 36K, |
Thanks! In that case, should I also allow asymmetric ports in split mode, or will that be a problem for the downstream flow? How should I deal with the attributes "rd_data_width" and "wr_data_width"? The names don't really make sense since the widths are per-port and each port can do read and write simultaneously. Are the timing differences only influenced by the port modes? Should it be something like port_a_width/port_b_width for 36k mode and port_a1_width/port_a2_width/port_b1_width/port_b2_width for split mode? |
Yes, we should allow asymmetric ports in split mode as well (18K bram). For SPRAM, one of the port was tied as write port and other port as read port, that the reason the attributes were named "rd_data_width" & "wr_data_width". But as you said each port can do a read and write, it would be appropriate have the attributes named based on ports, as you mentioned above port_a_width/port_b_width for 36k mode and port_a1_width/ port_a2_width/port_b1_width/port_b2_width for split mode. Only for the FIFO's there is a restriction that the write port (Push) will always be tied to Port A and the read port (pop) will always be tied to Port B, as the FIFO flags are tied to the Port A read data port. Anyways, inferencing is not supported for FIFO's only FIFO instantiation is supported. Timing difference is influenced by port widths, split/nonsplit and fifo modes. |
One question, earlier you mentioned that we won't be able to reuse the existing ql_bram_split pass and you would write a replacement, so if a designer writes a dual port ram like the one in bram_tdp and bram_tdp_split (with separate read and write address each port) would it be inferred ? |
No, I think those descriptions would require a primitive with at least 3 ports (2 write + 1 read) to map the semantics. An additional read port could be created with duplication, but there's no way to create more write ports than the primitive has, and if both are used to write then there is no port left over to read... |
Ok, understood. |
I've gone through the existing tests and rewritten the modules that infer memory with the patterns that memory_libmap expects. However the manually instantiated modules still use the old code path (bram_map.v + bram_final_map.v) which now has some issues as a) they are not considered as candidates for merging two 18k blocks in split mode, and b) they still use the rd_data_width/wr_data_width attributes vs the port_a_width/port_b_width. How would you like to handle this? Are all of the modules like BRAM2x18_TDP considered user-facing primitives that should continue to be supported? (FYI I've also had some simulation issues with the included sim tests where sdffsre cells would always output X, so I've had to rewrite the bram_tdp examples in a way that changes the behavior of the module to not infer FF cells, that's why it has those intermediate wires for ce now.) |
With regards to the manually instantiated modules, I will make the changes to use the port_a_width/port_b_width instead of rd_data_width/wr_data_width attributes. Also, I would look into using the libmap_brams_map.v or integrate manually instantiated modules into this file. The user facing primitives would be the following:
|
I have been running tests with latest PR (#2), few observations: I see that the Address and the Write data inputs to the RAMs are registered. Why is it registered? I have attached the design here with the synthesis output files. |
If you run memory_libmap with debug it will tell you:
Alternatively you can add the no_rw_check attribute to suppress collision handling: |
Were you able to test the PR sufficiently? Is there something else you need me to do on it, or should I get started on reviewing the other parts of |
Yes, we have tested the PR, you can proceed on reviewing the other parts of synth_quicklogic. |
@nakengelhardt 7.36. Executing MEMORY_LIBMAP pass (mapping memories to cells). I have attached the design and the synthesis report. Can you please help us understand this issue? Earlier with MEMORY_BRAM pass, the ram inferring was happening without any issue on this design. |
That memory is explicitly designated with |
I've updated |
Thank you! |
@nakengelhardt |
On the user side it would be assignments in For implementing the support, the information required is how the ram primitive expects the data to be provided. In |
The RAM primitive expects the data in the following format: Split=1 (18K RAM) x1: NonSplit=1 (36K RAM) x1: |
@nakengelhardt |
@rakeshm75 Thanks for the information! I just got back to the office yesterday, this is my next priority to work on. In the module definition of Also, in addition to the order of the bits for one line in the RAM, could you specify the order of the memory lines in the parameter value? |
@nakengelhardt |
Hi @rakeshm75, I am following up on @nakengelhardt's work on this. I opened a PR to add the I will be working on adding synthesis support for this next. In the document, you ask
In principle it can. If implemented, it should probably be enabled by passing a separate option to |
Hi @povik Yes, it would be consumed by the loader later. The purpose of the initialization data as hex file is for the loading/programming the initialization data through the pre-load bus. Regards, |
@nakengelhardt I have attached the design and synthesis report for both the runs (MEMORY_LIBMAP & MEMORY_BRAM). |
@rakeshm75 You can define TDP36K to have read-first behavior across the dual ports by adding |
Looking into the frontend code, Verific always takes memories to be read-first, no matter the input code. |
I'm not well versed in VHDL, but I think this is a collision handling problem. By analogy with the verilog pattern, I've found these options for having it infer the block ram:
There's a separate issue with distinguishing between read first and write first patterns with the verific frontend that I need to look into... |
@nakengelhardt Yes, with the above attribute (no_rw_check ) or with the code to explicitly handle the collision, Yosys infers the BRAM correctly with VHDL as well. |
@rakeshm75 I checked and it doesn't infer the equivalent verilog code either. IIRC the previous example had two different clocks, whereas in this case both ports are using the same clock, so it also needs to make sure that address collisions between Regarding the issue of verific only inferring read first ports even for other patterns, we have merged a fix today: YosysHQ/yosys#3927 |
To try to give more detail on when emulation is possible: Both the emulation of read-first and write-first collision behavior requires to delay the writes on the underlying hardware primitive by one cycle. In general that's not possible to implement on a shared R/W port since that can't be fitted into the limitation to operate on a single address per cycle. With two synchronous R/W ports that share a clock domain and require emulation of read-first colllision behavior with respect to each other, the inference code just can't find a way to map it onto TDP36K. The inference explores many options, so if there's e.g. one R/W port and one R port with the read-first requirement, then it finds it can implement that by duplicating the memory, performing writes simultanously on both instances in parallel, and using one instance for one read port and the other for the other port. |
@nakengelhardt , @povik |
@rakeshm75 I sketched something in PR #10, let me know how well that would work for you. |
I have attached ROM design (VHDL) here. How do we force Yosys to infer BRAM? |
@rakeshm75 I think you might have submitted the comment before the file upload completed? I am only seeing a link that goes back to this issue... |
@nakengelhardt |
Thanks, I have investigated the example. The constant is not its own element in the netlist so we can't see attributes from it, but we don't currently look at attributes from the output register, which we should. I will look into modifying |
@nakengelhardt @povik |
The description in the libmap_brams.txt file represents only the TDP mode. Since SDP is just a subset of TDP where some signals are unused, it's not considered a separate mode. If you want to infer SDP only, that requires writing new |
@nakengelhardt, we are still going to support use of 36k BRAM as two 18K BRAM in split mode. Nothing changes on that front. In fact, the external pin interface of TDP36K remains unchanged. We'll still have 4 ports on 36K BRAM. The only change is that we don't support True Dual Port mode. |
To confirm, before I start working on modifying
|
@nakengelhardt Here when we want to define ports separately for write and read, the names should be "W" or "WR" and "R" or "RD" correct? we cannot use "A" and "B" ?? I made the changes as follows and also modified the port names in libmap_brams_map.v accordingly (ex... PORT_A_WR_DATA -> PORT_W_WR_DATA, PORT_B_RD_DATA -> PORT_R_RD_DATA ...)
|
@nakengelhardt |
@rakeshm75 You can choose the names freely, if you prefer "A" and "B" you can name them that (I was just thinking it would be easier to read the code in the techmap file if the name also implies the capability). If you want to support two devices with different memory blocks, I would recommend having two libmap files (e.g. bram_tdp.txt and bram_sdp.txt), and then passing one or the other when calling |
@rakeshm75 I see that you still have |
@nakengelhardt |
@nakengelhardt Actually on the BRAM primitive there is no write clock enable and no read clock enable. On read side there is Read enable signal (REN). |
Yes, I needed to make it a clock enable for the R/W ports because you told me that if WEN is high the primitive will also update the read value, and the only way to represent that in the libmap logic is as clken. But if the ports aren't used simultaneously for read and write then there isn't any need to have a second signal because there is only one behavior (either the port is active or not). You can have |
Would you want me to prepare to add support for the sdp-only architecture to the upstream |
The text was updated successfully, but these errors were encountered: