Jun. 12th, 2022

pheloniusfriar: (Default)
Now comes the crux of this (temporary... two more to go) series of "porting" my posts from PSoC user forums to here: I had a question, got community help, and the final answer is absolutely beautiful and elegant. As I stated at the end of the thread to the person who provided the solution: "I used a sledgehammer to solve the problem, and once the requirements were clear, you used a feather". I really flailed with this because I failed to understand a key concept on how the TCPWM component worked. I was working with this component again recently and I thought of this exchange, and it motivated me to check to see if it was still there, and that fear of Dark Ages information loss prompted me to put in the effort to copy over all of my posts from there.

I have been trying to get a glitchless PWM working on a PSoC 4200 (I had in the past and was dusting off the old project), and I cannot seem to get the PWM Swap to work no matter what I try. I have attached a project that instantiates a PWM. I have the Line output connected to an LED so I can see the output of the PWM (it's on a CY8CKIT-049-42xx, so port 1.6), and am driving the PWM clock with a 1Hz clock so I can easily time the LED changing. In the configuration for the PWM component in PSoC Creator 4.3, I set the Period to 9 (I want a period of 10 seconds, and the datasheet says to set it to N - 1... that seems to work fine). I set the Compare Register to 2, I set the Compare Register Buffer to 6, and I turn on the Swap flag. The PWM is set to left align mode as well, and have turned off the interrupt. I'm keeping it simple with my example. The code in main does two things (my component is named PWM): "PWM_Start(); for(;;) CySysPmSleep();" ... so start the PWM component and put the processor to sleep. This is the simplest configuration I could come up with and it still doesn't work for me.

I program it into the 4200 and ... the LED stays on for 2 seconds, stays off for 8 seconds, stays on for 2 seconds, stays off for 8... etc. The Compare Register Buffer value never gets swapped with the Compare Register at TC per the documentation. I have tried a very large number of things including using the software to also set the swap flag to on ("PWM_SetCompareSwap(1);"), I've checked that the "ov" output is pulsing every 10 seconds (yup). In the component configuration, I have changed the Compare Register value to 4 and the Compare Register Buffer value to 8 just to make sure I'm programming the chip, and ... on for 4 seconds, off for 6 seconds, etc... never swapping in the buffered value. I've looked at all the documentation I could find. I've tried turning on the "switch" input and connecting it to the "ov" output (with a falling edge trigger). Nothing has worked. I set up an interrupt handler (not in the attached example) and verified that it was being called at TC... yup... I could write a value into Compare Register Buffer from the interrupt handler and ... it never gets swapped into the Compare Register, so no matter what I write, the PWM only reacts to the initial value of the Compare Register.

I have a running application on an identical PSoC 4200 that uses this technique, but compiled it with a much older version of PSoC Creator (probably something in the 3.x timeframe) and I just duplicated the code over for this project, and it definitely does not work anymore. Has something in PSoC Creator broken, has the component implementation in PSoC Creator changed, am I missing something very simple? I know it's not the hardware having changed, because it's the the same chips that used to work. What is particularly galling is that in the PWM component configuration window, it actually shows the expected waveform from the Line output, and it shows it high for 2 clocks, and then low for 8, then high for 6 clocks, and then low for 4. That does hint that something has gone wrong somewhere. I'm stumped. Any thoughts?


I got a response from a member of the community [MotooTanaka] on what they ended up doing to try to get it to work. It was WAY more effort than I could have possibly expected, and even then wasn't the direction I wanted to move toward (it required software, and I wanted a purely hardware solution if possible). Note: It's interesting in that it uses the serial port out to the PC as a status output, which is a good thing to remember that I can do when debugging (I forget sometimes).

Yes, this was(is) a very tough one. Just like you I tried for a few hours with series of failures... And in the datasheet of TCPWM, I found the following description:

void TCPWM_SetCompareSwap(uint32 swapEnable)

Description:
Writes the register that controls whether the compare registers are swapped. When enabled in Timer/Counter mode (without capture) the swap occurs at a compare/capture event. In PWM mode the swap occurs at the next TC event following a hardware switch event. Not applicable for Timer/Counter with Capture or in Quadrature Decoder modes.

Parameters:
uint32 swapEnable: 0 = Disable swap; 1 = Enable swap.


This sounds like we need a "switch" event before the "TC" event. So I made the following project, using CY8CKIT-042. Note: Actually the following is the only project I could make the swap work. And in the ISR I needed to put CyDelay(2) to generate a long enough pulse width from the Control_Reg. Probably it could be done in the main loop. After that I tried a several hardware approaches but in vain. So my current conclusion is that we seem to need provide "switch" event before TC event, and it must have enough pulse width for PWM to detect (or set something inside).

Schematic, pinouts, source code, and log output behind cut )


Again, I wanted a purely hardware solution, but... this laid the foundation for the solution. I wrote back:

I am overwhelmed by how much work you put into this. Thank you so much!

My frustration was that I definitely had it working, and it definitely was not working in this case. The documentation is quite contradictory, but your statement that a "switch" event is required matches my previous experience (and probably some random note I found somewhere when I was doing my earlier design). Your investigation and success doing it with an interrupt routine in software, led me to figure it out. Your statement that "I needed to put CyDelay(2) to generate a long enough pulse width" was what allowed me to find the relevant pieces of documentation that explained the situation.

The first piece of relevant information is at the bottom of the "Outputs" section of the TCPWM datasheet: "The overflow (ov), underflow (un), and compare/capture (cc) output signals have two HFCLK cycle pulse width for PSoC 4100/PSoC 4200 devices". So the "ov" output pulse is only two HFCLK clock cycles wide. The other needed piece of information is at the bottom of the "Inputs" section: "All inputs are double synchronized in the TCPWM. The synchronizer is run at HFCLK speed. After that (just for PSoC 4000, PSoC 4100, PSoC 4200, (Timer/Counter, PWM modes)), these signals are synchronized with the component clock." So... the "switch" input is sampled on the rising edge of the PWM "clock" signal, which in my case for this test is 1Hz. Since the "ov" is such a short pulse, it is long, long gone before it can be sampled by the 1Hz input clock if the "ov" signal is just looped back to the "switch" signal (which is what my original design that worked did). I should further mention to anyone reading this that the description of the Compare Swap feature in the datasheet is wrong. It currently says, "the swap selection causes the two compare values to swap at each TC event"; but it should say "the swap selection causes the two compare values to swap at each TC event if a switch event has occurred".



Then there's the question of why my earlier design did work. The answer to that makes sense now as well: I was clocking the PWM at HFCLK (48MHz in my case), so in looping the "ov" signal back to "switch" worked fine at triggering a switch event. The "ov" pulse is two HFCLK clock cycles long, and I had the "switch" configuration parameter set to trigger on a falling edge pulse, so the "switch" input would properly sample the high pulse because it was high long enough to be latched by the HFCLK clock signal, and then would properly sample the "ov" output going low again, and would trigger a switch event for the next TC. It would work as well if I had it set to switch on a low to rising edge. It seems I was just lucky with my earlier design because I didn't realize the "ov" signal issue -- I assumed it was clocked out with the PWM clock and would be high for one full PWM clock cycle (I was wrong on the number of clock cycles too since it is high for two HFCLK cycles) as this makes more sense to me as it allows for a full and easy hardware implementation of the register swapping at lower PWM clock frequencies. I think Cypress designers made a bit of a mistake with this decision to use an HFCLK-based pulse rather than a signal clocked with the PWM "clock" signal. Oh well, they're not going to be able to change it now as this is a hardware component and it can be made to work.

I really do want to do my design in hardware, so I figured out a way of using the on-chip programmable logic to allow the "ov" output to generate a signal usable by the "switch" input circuitry. This is an all-hardware solution as well. The Period has to be set to at least 2 (N - 1), so the PWM counts for at least 3 PWM clock cycles. The switch input is set to be a rising edge triggered event.The software is the same (turn on the PWM component and put the processor to sleep). Project attached.



There is definitely a chance for metastability issues since we don't know precisely what the phase relationship is between the HFCLK and the PWM "clock". To that end, I invert the "ov" signal before clocking the first flip-flop with it. This guarantees at least two HFCLK cycles between when the PWM clock goes high (to trigger the "ov" signal) and when the "ov" signal goes low again at the end of the pulse. Depending on how the "ov" pulse is generated, it could be three or more HFCLK cycles before the falling edge (since it is a signal potentially going between clock domains, they probably have a synchronizer on it). As long as half the cycle time of the PWM clock is longer than the maximum time between the rising edge of the PWM clock and the falling edge of "ov" (plus the time needed for the signal to propagate through the first flip-flop to its Q output and the setup time needed for the second flip flop before it is clocked by the low-going edge of the PWM clock), then there will be no metastability problems. Because I'm not sure what the worst case is for when the "ov" pulse happens after the PWM clock triggers the signal (by causing the count to roll over from TC to 0), I can't make a guess at what the maximum safe frequency would be for this circuit. I'm sure it's fine into the MHz region, but I can't know for sure.

Here's the timing diagram for the above circuit:



Depending on the PWM clock frequency you need, if it's high enough and there's a worry about metastability, just clock the PWM at HFCLK and use the prescaler setting to bring the frequency down to where you need it. Since the prescaler can go from 1 to 128 (in powers of 2), use HFCLK/128 as the upper limit of where you might need to use this circuit. So for a 48MHz HFCLK, upper limit would be 375kHz (and for anything below that you probably need to use this circuit to get the switch swap to work). I have attached my modified circuit. Note that the project generates two warnings because PSoC Creator 4.3 recognizes that the circuit goes between clock domains without proper synchronization. As described above, the timing is okay as long as the PWM cycle time is sufficiently long compared to when the falling edge of "ov" occurs.

With these high and low speed techniques available for doing the Compare Register swapping, it can be done with any PWM clock frequency. Either allows for the glitchless operation of the PWM where the Compare Register Buffer can be updated in an interrupt handler triggered by the PWM TC event. The Buffered value is then automatically and cleanly swapped into the Compare Register at the TC event because a switch event happened in the last cycle. As long as the interrupt handler can run before the TC (in my earlier project, I used a Period count of 2177 at 48MHz, and woke the CPU up from a CySysPmSleep to process the interrupt, and there was plenty of time). The code I used in my old project to do this was as follows. I, of course, turned on the Interrupt on Terminal Count option on the PWM component and attached an Interrupt component (called "PWM_TC_ISR" in the code here).

CY_ISR(PWM_Next_Value) {
    PWM_WriteCompareBuf(<>);
    PWM_ClearInterrupt(PWM_INTR_MASK_TC); }

int main() {
    PWM_TC_ISR_StartEx(PWM_Next_Value); PWM_Init();

    // Write the first two samples into the PWM
    PWM_WriteCompare(<>);
    PWM_WriteCompareBuf(<>);

    CyGlobalIntEnable; PWM_Enable();  // Only starts it, does not re-initialize
    for(;;) { CySysPmSleep(); } }

Thank you again very much for your help!


To which the community member goes S-Tier and answers:

Thank you very much for your throughout explanation! Finally I understood with what we were fighting yesterday. BTW, reading your "paper", following (a kind of) stupid Idea came to my mind. At first I thought that I'd use a counter to generate the switch event. But we need to avoid immediate after the TC, and the width must be 2 or greater. Seeing the PWM configure dialog,



I thought... didn't we have a "counter" here? So if, and only IF, you can keep the compare between 2 to period-2, line_n seems to work for the purpose. So I modified the schematic as:



And... it works!



As I wrote above this trick works only 2 ~ period-2, but if your application put up with this limitation, this is quite an easy trick. Last but not least, I agree with you that the description in the datasheet was not kind nor sufficient for usual people like us.


Mind. Blown. So simple, so beautiful!

Hahahaha, I used a sledgehammer to solve the problem, and once the requirements were clear, you used a feather! That is definitely the optimal solution for the Compare register swap problem in almost all cases (provided, as you say, one can live within the compare value limitations), and it requires no additional system resources.

If you use a "Falling edge" trigger instead on the "switch" input, there is no limitation on the upper value for the Compare value (it can equal Period). Also, because the Period is actually set to N - 1 of the number of cycles (N) you want to count to [from the Period description for the PWM mode in the datasheet: "to cause the counter to count for N cycles, this register should be written with N-1 (counts from 0 to period inclusive)"], you can also go as low as 1 in the Compare register. I tested this with your loopback configuration and the Falling Edge trigger of "switch" with a Period of 9 (10 clock cycles), with a Compare Register of 1 and a Compare Register Buffer of 9, and it worked fine. I have attached the project.

Thank you again for providing a truly elegant solution to this problem.


And this, my friends, is how community can work together to solve hard to understand problems in elegant ways.

And here's more entertainment... Season 1, Episode 22:

pheloniusfriar: (Default)
The penultimate PSoC technical post "port" from the community forums to here for my own use. I'm kind of proud of this one because it's a really deep dive into technologies that are a little bit "black box" and this was some intense exploration on my part. If anyone else has done this, they certainly hadn't published anything.

I wanted to do this, but could not find any reference (one way or the other) to being able to use the Parallel Input (PI) to the datapath as a constant for calculations with the ALU (e.g. add or subtract), so I decided to give it a try because this would be very helpful in one of the things I'm trying to do with UDBs.

The answer? Yes, you can use the PI as a constant! I have attached a simple project to demonstrate this (I did it on a CY8CKIT-049-42xx, you'll need to reconfigure the Bootloadable component with your elf/hex files for it if you try to compile it for the same target).

Since there's not a lot of documentation on using the PI, much less in this manner (I really couldn't find anything that mentioned using PI as a constant, but my search skills may not be 100%), I thought I should document what I did and share it with others. Following the general steps outlined in AN82156 "Designing PSoC Creator Components with UDB Datapaths", section A.5 "Project #5 – Parallel In and Parallel Out", I created a component symbol, added a parameter called "PI_Value" that I set to 5 as default, and created a blank Verilog file for it. The parameter constant was added automatically when the Verilog file was created from the symbol editor, fyi. I then used the Datapath Configuration Tool per the instructions in AN82156 and created a blank cy_psoc3_dp datapath instance in the Verilog file. In the tool, first enabled the PI_DYN bit, then set it to EN. I created two datapath instructions: one to add the value of PI to the value of A1 and then store it back in A1 (FUNC = ADD, SRCA = A1, SRCB = A1, A1_WR_SRC = ALU, and CFB_EN = ENBL... the last of which dynamically selects PI as the input for SRCA to the ALU rather than A0 or A1 when the static PI_DYN bit is EN), and the other to just pass the value of A1 through the ALU (with FUNC = PASS, SRCA = A1, SRCB = A1, CFB_EN = DSBL). My Verilog then just flips between the two states every clock. The idea was that the first state would add 5 to A1 every time it ran, and that's what it does (the second state does nothing, but I wanted to have something heading into different states to make it a bit more representative). To finish up the datapath configuration in the Verilog file, I made the following assignments: .clk(Clock), .cs_addr( { 1'b0, 1'b0, next_state } ), .pi(PI_Value), and .po(po), where next_state was a simple flip-flop "reg next_state;"). So .pi() was passed the constant I had defined at the component level. There's a bit of wiring around the Parallel Output (PO) because I have a little LED board I made so I could watch the state transitions on port P2[7:0] (and that's why the frequency is so slow as well... so I could easily count the binary to make sure it was increment by 5). Note, writing ".po({Out7,Out6,Out5,Out4,Out3,Out2,Out1,Out0});" also seemed to synthesize fine and saved that extra wiring and assign statements (I didn't test it out on hardware though). I was trying to emulate the style of AN82156.

So, there were two things I wanted to mention before wrapping up. The first is that the way PO works is not immediately clear from the basic datapath diagram that appears in a lot of the documentation (e.g. the TRM, Rev *H, Figure 16-6 "Datapath Top-Level"). The implication is that PI, A0, and A1 are multiplexed onto SRCA to the ALU, and that PO is connected to the SRCA connection itself (this is literally how it's drawn in that figure). That was one of the reasons why I initially made two different datapath states: because I thought that in the ADD state, since I was routing PI to SRCA, that I would see it on PO. I added the second state so A1 would be applied to SRCA and PO would then alternate between PI and A1 (I needed to see A1, but seeing the constant every second cycle was fine). It didn't work that way... all I got out of PO was the value for A1. I had to dig deep into the TRM to find the answer, but in section 16.2.2.8 of the TRM (Rev. *H) "Datapath Parallel Inputs and Outputs", there is another diagram (Figure 16-25 "Datapath Parallel In/Out") that shows the insides of the SRCA multiplexer. In this diagram, it is shown that there are two multiplexers: one that selects between A0 and A1 that feeds into a second multiplexer whose other input is PI. PO is connected to the output of the first multiplexer... so that PO can never see the value of PI, and even when PI is being used, PO will be connecting to either A0 or A1 only. Mystery solved. But ugh.

Lastly, there seems to be a couple of bugs in AN82156 "Designing PSoC Creator Components with UDB Datapaths", section A.5 "Project #5 – Parallel In and Parallel Out". It assigns two bits to a one bit register ("state"), and then suggest a non-blocking assigning of the PO value out of the datapath to the component's output:

// From AN82156 ... bugs?
reg state;
wire[7:0] po;

localparam STATE_LOAD = 2'b00;
localparam STATE_ADD = 2'b01;

always @( posedge clk )
begin
  case (state)
    STATE_LOAD:
      begin
        state <= STATE_ADD;
        /* we must latch the PO value here, because in the next state PO is not valid*/
        Parallel_Out <= po;
      end
  STATE_ADD:
    begin
      state <= STATE_LOAD;
    end
  endcase
end

But Warp would complain (rightfully so I think) when I tried it with my component, that Out7 through Out0 were "not a register type", and could not be assigned in that way. I may be doing something wrong (let me know if you know what it is), since I am no expert at this.

// *** Does not work!!! ***
wire [7:0] po;
reg next_state;

always @ (posedge Clock)
begin
  case(next_state)
    1'b0:
      begin
        Out7 <= po[7]; Out6 <= po[6]; Out5 <= po[5]; Out4 <= po[4];
        Out3 <= po[3]; Out2 <= po[2]; Out1 <= po[1]; Out0 <= po[0];
        next_state <= 1'b1;
      end
    1'b1:
      begin
        next_state <= 1'b0;
      end
  endcase
end

In my case at least, fIguring out how to use UDBs is hard enough without these couple of additional issues.

One last thought (I haven't tried this yet, it's next on my list). Since the the SUB function of the ALU is "SRCA - SRCB", if A1 is SRCB, it is not possible to subtract PI from it (and store it back in A1, for instance). However, if PI is stored as an 8-bit 2's complement negative number and is added to the A1 register instead of subtracted, then the result will be that the PI value is effectively subtracted from the A1 value. I need to figure out the flags, but this seems like it should work fine. Again, I haven't tried it, but maybe setting the parameter as an int8 instead of a uint8 will allow negative numbers and will encode it properly.

Edit 2020/09/10: I did try what I suggested with the 2's complement arithmetic and it worked like a charm. For instance, to subtract 5 by using the ADD function of the ALU, I set PI to -5 in 2's complement (".pi(8'b11111011)") in the datapath setup, and tried ADDing it with a few values like 10, 5, and 3, and I got the correct answers (which was expected). I wanted to know what the flags were set to so I could figure out the best way to do 16-bit arithmetic on a single 8-bit datapath, so I used A1 as the MSB and A0 as the LSB. What is extra nice, is the state of the carry out bit is 0 if the "addition" of the 2's complement PI to A0 results in a negative number, and 1 if not. I used the registered carry flag input to a DEC command on the MSB register (A1), and it performed the operation A1 <- A1 - 1 + carry (carry is 1 if ADD PI + A0 is a positive number, so the DEC A1 command with the registered carry option gives A1 <- A1 - 1 + 1, so A1 doesn't get decremented; but if PI > A0 and the operation ends up with a negative result, the operation is A1 <- A1 - 1 + 0, which decrements A1). So this can be used for 16-bit arithmetic on the 8-bit ALU with two cycles! I double-checked the SUB command on the ALU, subtracting A0 <- A0 - A1 for instance, and the carry flag (which the spec says is "inverted" for SUB results, but I wasn't sure what that meant exactly), let's you use the DEC A1 command with registered carry again to do proper 16-bit math. Just fyi.

Edit 2020/09/14: Short summary: this technique uses a macrocell! Long summary: I was working on my component that used the above technique and I kept seeing an extra macrocell being used. I had declared 4 registers, but 5 macrocells were being allocated to the design and it was driving me nuts trying to figure out what was causing it. I had assumed I had messed up an if or case statement or something and was causing an implicit latch instantiation, but nothing I did seemed to make any difference (and from what I can tell, Warp doesn't care if you're missing a final else statement, or if you haven't specified all possible cases in a case statement... it doesn't create implicit latches like other synthesizers in those cases it seems). I eventually broke down and started rummaging in the PSoC Creator file output and got my answer. It turns out the extra macrocell was used to provide the value to the Parallel Input (which is why I mention it here). In the "codegentemp" there was a file _p.vh2 that is the VHDL translation of the Verilog code it looks like. In it, there was a macrocell definition called "__ONE__". The only thing this macrocell does is supply logic 1s to the appropriate bits in the value provided to the Parallel Input. If I set .pi(0) in the datapath instantiation, the macrocell goes away. I had thought that the constant bits would be supplied through a connection with the routing channels (like there was a magic source of 1s and 0s available on it), but it seems that a macrocell in the UDB is needed to supply that logic level. Fair enough, but it's good to know that one of the macrocells will be used if you use the Parallel Input to supply a constant value. I have enough to spare, but was worried it was something more potentially vexing. I don't know where the 0s are coming from (the PI bits that were logic 0 were not connected in the netlist to anything), but I'm not too concerned, it works fine. In the code below, it was set as ".pi(8'b11111011)".

\TEST_COMP:DP_INST\:datapathcell
        GENERIC MAP(
            [ ... bunch of config deleted ...]
            uses_p_out => '0',
            clk_inv => '0',
            clken_mode => 1)
        PORT MAP(
            clock => Net_35_digital,
            cs_addr_2 => \TEST_COMP:state_2\,
            cs_addr_1 => \TEST_COMP:state_1\,
            cs_addr_0 => \TEST_COMP:state_0\,
            ce0_comb => \TEST_COMP:lsb_equal\,
            z0_comb => \TEST_COMP:lsb_zero\,
            ce1_comb => \TEST_COMP:msb_equal\,
            z1_comb => \TEST_COMP:msb_zero\,
            p_in_7 => __ONE__,
            p_in_6 => __ONE__,
            p_in_5 => __ONE__,
            p_in_4 => __ONE__,
            p_in_3 => __ONE__,
            p_in_1 => __ONE__,
            p_in_0 => __ONE__,
            busclk => ClockBlock_HFClk);
    __ONE__:macrocell
        GENERIC MAP(
            eqn_main => "1'b0",
            regmode => 0,
            clken_mode => 1)
        PORT MAP(
            q => __ONE__);

I am getting closer to being done now that this is resolved as well.


And the only response was a thank you from a mod for sharing the info with the community (which is cool, don't get me wrong).

And now, on with the show: Season 1, Episode 21:

pheloniusfriar: (Default)
And finally... a question that did not receive an answer. I'm thinking there might be a way of doing this, but I would need to hear from someone who knows the innards of the PSoC Creator Integrated Development Environment (IDE) on how unless I stumble on some poorly documented back door way of doing it. It maybe impossible from within the component itself though; but it's theoretically possible, and I would view this as a shortcoming of the tool if it's not. Sadly, the tools at Cypress started to balkanize even before they were bought out by Infineon and I'm not getting strong "we'll be supporting these tools going forward" vibes. I'm not plugged in, but it has been over 2 years since the last PSoC Creator update. Not looking good.

Why do I want to do this? If I could get this to work, I could package a UDB-based PSoC component for PSoC Creator to do 16-bit arithmetic with a single 8-bit UDB. I'm a fan of pushing technologies as far as they'll go, so this was a little side project I was working on that was part of a larger project.

I have a UDB component (single UDB) and was wondering how to initialize the associated Auxiliary Control Register from within the component itself without having to do it at the project level with a call to an initialization API function in main() or some other project-level technique? I want to configure the FIFOs of a UDB into their "Single Buffer" mode by writing 0x03 into the Auxiliary Control Register of that UDB (Architecture TRM, section 16.2.3.7), but can't figure out a way of doing that automatically.

Edit 2020/09/16: To clarify, I am trying to initialize that register at the component level, rather than at the project level, so that someone using the component just has to drag it onto their schematic from the component library, and all the necessary hardware configuration would be done transparently by the component itself simply because it has been instantiated (and not require an initialization function to be called in main() or for the startup code to be modified at the project level). This is the case already for everything but that Auxiliary Control Register. The notion was to make the component potentially a pure "hardware" component from the perspective of someone using it in their project from the component library (it has some parameters that can be set from the schematic, and i was hoping that was all that would needed to be done by someone using it).

I saw in the Component Author Guide the ".cy_registers()" function (section 4.3.5 "Fixed Blocks") that "For all fixed blocks, a parameter of cy_registers is available to allow you to explicitly set the values for the given registers for the block. The values listed will be included in the configuration bitstream generated to program the part, which will be established before the main() function is called." This seems to be exactly what I want, but it doesn't seem that the datapath itself allows this parameter. When I look at the .vh2 file created for the component, the instance of cy_clock_v1_0 that I put on my schematic (and wired to my component) has a parameter "cy_registers" (it's empty, but it's listed); but the cy_psoc3_dp I instantiated does not have such a parameter listed. When I tried to add it to the parameter list of the datapath, I get an error.

Is there a "proper" way of doing this from within my component so the value is automagically intialized by the time main() runs? I wanted the component to be able to run without needing to be initialized in the user's software (effectively a pre-configured hardware component), and the FIFO configuration is the only thing I can't seem to access from within my Verilog code (and it won't work without the FIFOs being in the proper mode). I hope I'm just missing something obvious.

I'm guessing that the cyfitter_cfg() function can do it (and is where this should end up), but I can't figure out how to get it in there (there's a BS_UDB_0_0_0_CONFIG_VAL[] array in the cyfitter_cfg.c file that was created that seems to have the UDB config in it, and presumably the Auxiliary Control Register is one of those values). I read through Alan Hawse's excellent IoTExpert article on PSoC startup, and it told me where it would be done, but provided me with no clues on how to get it done.

PSoC4 Boot Sequence (Part 5) – Initializing the PSoC with initialize_psoc()


[I'm thinking I should probably save that Blog before it gets deleted some day as well... it's irreplaceable information]

A user [RodolfoGL] tried to help, and posted:

I don't think it will make any difference setting the FIFOs during startup or in the main(). In any case, if you want to customize the startup code, you need to import the cy_boot component. See how to do here:

PSoC Creator Tutorial - Importing and Copying Components - YouTube

Then open the PSoC4/API/Cm0Start.c to do your changes.


I wrote back:

Thanks for the suggestion. I watched the video and thought about it, and that isn't what I want to do. When I was asking my question, I wasn't entirely sure I was being clear, and I was not (I will edit my question to clarify). As you say, doing it this way, or having an initialization function that gets called by the user in main() makes no difference because it is happening at the project level. What I realized I should have said was that I am trying to initialize that register at the component level, so that someone using the component just has to drag it onto their schematic, and all the necessary hardware configuration is done transparently by the component itself (and not require an initialization function to be called in main() or for the startup code to be modified... although it's good to know that technique for my own projects now that you've pointed it out to me). This is the case for everything but that Auxiliary Control Register. The notion was to make the component potentially a pure "hardware" component from the perspective of someone using it in their project from the component library (it has some parameters that can be set from the schematic, and i was hoping that was all that needed to be done by someone using it). Thanks for helping me refine my question.

To which they replied (kind of confirming where I was stuck):

I understand now what you want. I'm not aware a way to do this automatically. I think the only way is to create an API for your component and let the user call it in the main().

So, I wrote again, hoping someone else might jump on when they realized what I was looking for (no takers):

That is what I'm suspecting as well, but am hoping I've missed something.

I am wondering now, looking at the wording, whether there's a back door I can use? Section 4.3.5 "Fixed Blocks" in the Component Author Guide, it says "For all fixed blocks, a parameter of cy_registers is available". In referring to the figure in section 4.3.1.1 "UDB Overview" it says "The blocks are color coded to differentiate between the types. Purple is a register, blue is a fixed block that performs a defined function, ...". The only blue block ("fixed block") that I can instantiate explicitly in my code is the cy_psoc3_count7 block, so I wonder if it will accept a cy_registers parameter? And if it does, will it check what register directive I pass in to it? The cy_registers parameter is not listed in the instantiation list in the guide for cy_psoc3_count7, so I am guessing this will not work, but I'll give it a try Wednesday (hopefully) and report back.

I scoured the Internet before I asked my question and have not been able to find a single reference to cy_registers, so I am not even sure how (or if) it is supposed to be used. The fact that it was listed in the "Implement a UDB Component" section suggests that there should be some way to access this capability from a library component's Verilog code. But maybe that's not the case, I don't know.


So I tried, and failed:

In a surprise to nobody, and despite the glimmer of hope from the wording in the Component Author Guide, the cy_psoc3_count7 component does not accept cy_registers as an argument.

This compiles fine:

cy_psoc3_count7 #(.cy_period(7'b1111111))

    C7Counter (.clock(CLK), .reset(1'b0), .load(1'b0), .enable(1'b1), .count(), .tc());

This results in the error, "'cy_registers' not a parameter of module 'cy_psoc3_count7'" during synthesis:

cy_psoc3_count7 #(.cy_period(7'b1111111), .cy_registers(""))

    C7Counter (.clock(CLK), .reset(1'b0), .load(1'b0), .enable(1'b1), .count(), .tc());

I guess unless someone from Cypress can help, I'll need to re-think my plans on this.


I kind of put the project aside because I couldn't get an answer, but hope to dust it off some day since it's a neat idea if I can make it work.

And last, but not least, Season 1, Episode 20:

Profile

pheloniusfriar: (Default)
pheloniusfriar

May 2025

S M T W T F S
    123
45678 910
11121314151617
1819202122 2324
25262728293031

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jul. 17th, 2025 09:27 am
Powered by Dreamwidth Studios