What's the difference between uvm_sqr_if_base::peek() and uvm_sqr_if_base::get_next_item()? - system-verilog

In System Verilog UVM, the interface present in TLM ports for communication between sequences and drivers (uvm_sqr_if_base) offers flexibility. For pulling requests, I'll summarize the four options in this table:
blocking item_done
get_next_item() yes no
try_next_item() no no
get() yes yes
peek() yes no
... where blocking indicates that if you call the method, but no sequence is ready to provide data, then the task will BLOCK and wait until data is ready and can and will be returned.
... where item_done indicates that the method also calls item_done() after successfully pulling an item from the sequence.
What's the difference between peek() and get_next_item() then?
Consider these two code snippets from two imaginary drivers ...
while(1) begin: loop1 while(1) begin: loop2
seq_item_port.get_next_item(req); seq_item_port.peek(req);
process(req); process(req);
seq_item_port.item_done(); seq_item_port.item_done();
end: loop1 end: loop2
What's the difference? Why are there two methods for accomplishing the same thing? It doesn't seem to be TLM1 vs TLM2 styles.
EDIT:
To answer the point below. I had looked at the source code before asking this question. In fact, the LANGUAGE differs only slightly, the code seems to do exactly the same thing. What's the point? What am I missing?
// Task: get_next_item
//
// Retrieves the next available item from a sequence. The call will block
// until an item is available. The following steps occur on this call:
//
// 1 - Arbitrate among requesting, unlocked, relevant sequences - choose the
// highest priority sequence based on the current sequencer arbitration
// mode. If no sequence is available, wait for a requesting unlocked
// relevant sequence, then re-arbitrate.
// 2 - The chosen sequence will return from wait_for_grant
// 3 - The chosen sequence <uvm_sequence_base::pre_do> is called
// 4 - The chosen sequence item is randomized
// 5 - The chosen sequence <uvm_sequence_base::post_do> is called
// 6 - Return with a reference to the item
//
// Once <get_next_item> is called, <item_done> must be called to indicate the
// completion of the request to the sequencer. This will remove the request
// item from the sequencer FIFO.
vs.
// Task: peek
//
// Returns the current request item if one is in the sequencer FIFO. If no
// item is in the FIFO, then the call will block until the sequencer has a new
// request. The following steps will occur if the sequencer FIFO is empty:
//
// 1 - Arbitrate among requesting, unlocked, relevant sequences - choose the
// highest priority sequence based on the current sequencer arbitration mode.
// If no sequence is available, wait for a requesting unlocked relevant
// sequence, then re-arbitrate.
//
// 2 - The chosen sequence will return from <uvm_sequence_base::wait_for_grant>
// 3 - The chosen sequence <uvm_sequence_base::pre_do> is called
// 4 - The chosen sequence item is randomized
// 5 - The chosen sequence <uvm_sequence_base::post_do> is called
//
// Once a request item has been retrieved and is in the sequencer FIFO,
// subsequent calls to peek will return the same item. The item will stay in
// the FIFO until either get or <item_done> is called.

Peek is definitely from TLM1 style. It gives you the opportunity to look at the next item in the queue without removing it from the queue. This means you have not committed to starting the item. Peeks could be used in two different scenarios.
One-to-Many: You could have multiple drivers, or multiple threads within a single driver doing peeks and then deciding if they want to get the sequence item, or ignore the item. The peak lets you look at the contents of the item before you make a decision to commit to it with a get.
Many-to-one: You could have multiple sequencers connected to the same driver. The driver would take a peek from all its connections and decide which sequencer it wants to commit to.

Related

How can I have different initial state for one agent type?

I'm working on an epidemic model. In this model, there are 2 types of susceptible states, S1 and S2. At the start of the model, I want there to be agents in S1 state as well as S2 state.
For example, I have total 1000 agents at first, and I want there to be 200 in S1 and 800 in S2.
I tried to set S1 as the start state and create a message transition from S1 to S2, then coded in main as:
for(int i = 0; i < 1000*0.8; i++)
send("s1 to s2", people.random());
But it will cause repeat message to the same agent, thus there won't be 800 in S2 state. Also I think it's not a good design.
You need some code to do selection-without-replacement so you don't choose the same agent multiple times. There's nothing built-in to AnyLogic to do this for you, so you'd want code something like the below (assuming your agents are of type Person).
// Create selection list with all Person agents in
List<Person> selectionList = new ArrayList<Person>();
for (Person p : people) {
selectionList.add(p);
}
// Select one 800 times (removing them from our selection list as we go)
for (int i = 0; i < 800; i++) {
int randomIndex = uniform_discr(0, selectionList.size() - 1);
send ("s1 to s2", selectionList.get(randomIndex));
selectionList.remove(randomIndex);
}
The crucial thing to remember (and something that many AnyLogic modellers don't understand) is that messages to statecharts are processed asynchronously (in hidden timeout 0 events which you can see on the Events View at runtime) however you send them (e.g., whether you use send, receive or the statechart's fireEvent function --- these just affect the number of such intermediate events and whether the "On message received" action of the Connections element is triggered or not).
Thus an agent will never have changed state in a block of code which sends messages to them. (It will only do so after this block of code --- and the current event it is triggered from --- completes.)
P.S. As per your comment to Emile's (incorrect) answer, the other way to do this is via dynamic events so that each message is sent after previous ones have been processed. But then you have to be very careful that you get the at-the-same-sim-time event ordering correct (loads of subtle detail here) and you'd still have to do the filtering in Emile's answer which is very wasteful; much easier to do it the more conceptually correct way where you whittle down the set of agents you're sampling from as you go.

Anylogic: Queue timeout & condition

In the default settings it is possible to set a time in the queue after which an agent leaves the queue via outTimeOut. However, only a fixed time can be entered in the corresponding field, e.g. 12 hours. Is there a possibility to link these 12 hours with a condition? In my case, the agent should only leave the queue after 12 hours via outTimeOut if a certain condition is also met. In my case, if a variable varIN == 1.
Collect the time in queue statistics for each agent. Create a parameter called entryTime. When they enter the block, set agent.entryTime=time();
You can create an event that iterates through the queue every 1 second and removes the agents that meet your conditions from the queue (by using remove(Agent agent) function). That means if (time()-agent.entryTime>12)&&(agent.varIN==1), you will remove that agent.
Loop will look like this:
for (int i=0; i< yourQueue.size(); i++) {
YourAgentType currentAgent = ((YourAgentType)yourQueue.get(i));
if ((time()-currentAgent.entryTime>12)&&(currentAgent.varIN==1)){
yourQueue.remove(currentAgent);
}
}
You can use a function (returning a double) to define and calculate the most complex logic you like. If you provide an argument of type Agent (or the specific agent type flowing through the blocks), it can even account for your agent characteristics.
In the queue block timeout, simply call the function.

Anylogic - Assembler should stop working for 2 hours after 10 assemblies done

The "Assembler" should stop working for 2 hours after 10 assemblies are done.
How can I achieve that?
There are so many ways to do this depending on what it means to stop working and what the implications are for the incoming parts.. but here's one option
create a resourcePool called Machine, this will be used along with the technicians:
on the "on exit" action of the assembler do this (I use 9 instead of 10 because the out.count() doesn't count until the agent is completely out, so when it counts 9, it means that you have produced 10)
if(self.out.count()==9){
machine.set_capacity(0);
create_MyDynamicEvent(2, HOUR);
}
In your dynamice event (that you have to create) you will add the following code:
machine.set_capacity(1);
A second option is to have a variable countAssembler count the number of items produced... then
on exit you write countAssembler++;
on enter delay you write the following:
if(countAssembler==10){
self.suspend(agent);
create_MyDynamicEvent(2, HOUR,agent);
}
on the dynamic event you write:
assembler.resume(agent);
Don't forget to add the parameter needed in the dynamic event:
Create a variable called countAssembler of type int. Increment this as agents pass through the assembler. Also create a variable called assemblerStopTime. You also record the assembler stop time with assemblerStopTime=time()
Place a selectOutputOut block before the and let them in if countAssembler value is less than 10. Otherwise send to a Wait block.
Now, to maintain the FIFO rule, in the first selectOutputOut condition, you need to check also if there is any agent in the wait block and if the current time - assemblerStopTime is greater than 2. If there is, you free it and send to the assembler with wait.free(0) function. And send the current agent to wait. You also need to reset the countAssembler to zero.

what's the application of sequential transmission of I2C in HAL library in STM32f746ng

I can understand that you can use first frame option for first frame and next frame options for others, but since you can use them as FIRS_FRAME_LAST_FRAME, what is the advantage of other? and when we must use them?
Findings:
A code use wile to continuously transmit two number and get a callback to see if module has accepted that, if this happen correctly the led must blink.
In this simple code I've tested every xferoption of sequential transmission, every options worked except: I2C_LAST_FRAME_NO_STOP and I2C_FIRST_FRAME.
Code:
while (1)
{
value=300;
*(uint16_t*) buffer=(value<<8)|(value>>8);//Data prepared for DAC module
HAL_I2C_Master_Seq_Transmit_IT (&hi2c1, (MCP4725A0_ADDR_A00<<1), buffer, 2,I2C_LAST_FRAME_NO_STOP);
HAL_Delay(1);
HAL_I2C_Master_Receive(&hi2c1, (MCP4725A0_ADDR_A00<<1), rxbuffer, 3, 1000);
if( (uint16_t)(((uint16_t)rxbuffer[1])<<8|((uint16_t)rxbuffer[2]))>>4 == value ){
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);}
HAL_Delay(50);
value=4000;
*(uint16_t*) buffer=(value<<8)|(value>>8);
HAL_I2C_Master_Seq_Transmit_IT (&hi2c1, (MCP4725A0_ADDR_A00<<1), buffer, 2,I2C_LAST_FRAME_NO_STOP);
HAL_Delay(1);
HAL_I2C_Master_Receive(&hi2c1, (MCP4725A0_ADDR_A00<<1), rxbuffer, 3, 1000);
if( (uint16_t)(((uint16_t)rxbuffer[1])<<8|((uint16_t)rxbuffer[2]))>>4 == value ){
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);}
HAL_Delay(50);
}
The HAL sometimes poorly documents these variables functions, and you will need to dive into the reference manual !
Looking at what the #defines are
https://github.com/STMicroelectronics/STM32CubeF7/blob/f8bda023e34ce9935cb4efb9d1c299860137b6f3/Drivers/STM32F7xx_HAL_Driver/Inc/stm32f7xx_hal_i2c.h#L302-L307
/** #defgroup I2C_XFEROPTIONS I2C Sequential Transfer Options
* #{
*/
#define I2C_FIRST_FRAME ((uint32_t)I2C_SOFTEND_MODE)
#define I2C_FIRST_AND_NEXT_FRAME ((uint32_t)(I2C_RELOAD_MODE | I2C_SOFTEND_MODE))
#define I2C_NEXT_FRAME ((uint32_t)(I2C_RELOAD_MODE | I2C_SOFTEND_MODE))
#define I2C_FIRST_AND_LAST_FRAME ((uint32_t)I2C_AUTOEND_MODE)
#define I2C_LAST_FRAME ((uint32_t)I2C_AUTOEND_MODE)
#define I2C_LAST_FRAME_NO_STOP ((uint32_t)I2C_SOFTEND_MODE)
We can see references to RELOAD and AUTOEND and SOFTEND.
Digging into the reference manual
https://www.st.com/resource/en/reference_manual/rm0385-stm32f75xxx-and-stm32f74xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf#page=969
So we can see here the reference to
AUTOEND - as a way to automatically implement a STOP condition after the set bytes end
SOFTEND as a way to prevent the automatic STOP condition and require the software to decide.
Relationship to your observed behaviour
The define's using the SOFTEND mode is where you saw things not working, and this is to be expected, the I2C protocol was not being fulfilled as there was nothing in the code to indicate the STOP condition.
So what does this mean you can do - an example of a variable byte i2c slave receiver
I haven't found a shining example from ST for this, but let me illustrate an example I have implemented in a project for an I2C Slave.
Let us look at the callbacks that are called:
https://github.com/STMicroelectronics/STM32CubeF7/blob/master/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_i2c.c#L76-L97
*** Interrupt mode IO operation ***
===================================
[..]
(+) Transmit in master mode an amount of data in non-blocking mode using HAL_I2C_Master_Transmit_IT()
(+) At transmission end of transfer, HAL_I2C_MasterTxCpltCallback() is executed and users can
add their own code by customization of function pointer HAL_I2C_MasterTxCpltCallback()
(+) Receive in master mode an amount of data in non-blocking mode using HAL_I2C_Master_Receive_IT()
(+) At reception end of transfer, HAL_I2C_MasterRxCpltCallback() is executed and users can
add their own code by customization of function pointer HAL_I2C_MasterRxCpltCallback()
(+) Transmit in slave mode an amount of data in non-blocking mode using HAL_I2C_Slave_Transmit_IT()
(+) At transmission end of transfer, HAL_I2C_SlaveTxCpltCallback() is executed and users can
add their own code by customization of function pointer HAL_I2C_SlaveTxCpltCallback()
(+) Receive in slave mode an amount of data in non-blocking mode using HAL_I2C_Slave_Receive_IT()
(+) At reception end of transfer, HAL_I2C_SlaveRxCpltCallback() is executed and users can
add their own code by customization of function pointer HAL_I2C_SlaveRxCpltCallback()
(+) In case of transfer Error, HAL_I2C_ErrorCallback() function is executed and users can
add their own code by customization of function pointer HAL_I2C_ErrorCallback()
(+) Abort a master I2C process communication with Interrupt using HAL_I2C_Master_Abort_IT()
(+) End of abort process, HAL_I2C_AbortCpltCallback() is executed and users can
add their own code by customization of function pointer HAL_I2C_AbortCpltCallback()
(+) Discard a slave I2C process communication using __HAL_I2C_GENERATE_NACK() macro.
This action will inform Master to generate a Stop condition to discard the communication.
Therefore, you could implement a I2C slave that could read a variable/dynamic amount of data:
Receive 1 byte - using the SOFTEND based options
This prevents the stop condition being raised, but once this first byte is received will trigger the HAL_I2C_SlaveRxCpltCallback().
In the HAL_I2C_SlaveRxCpltCallback() check the value of the first byte and then request more data of any further length, but this time using an AUTOEND based option.

How do I choose both exact quanitity wait for and quanitity available in the same pick-up block?

So I have a vehicle which is going to pick up exact quantity (Wait for) the first 50 minutes in the simulation. After those 50 minutes have gone by, I want the same vehicle to pick up quantity (if available). How do I go about this?
Alternative approach (avoiding complex Java coding) is to use 2 Pickup blocks, each with a different setup. Put a SelectOutput block before them and route agents into the respective block using time()>50*minute() in the SelectOutput condition
Set it up for the first setup by default.
Create an event to trigger after 50 mins and make it execute this code:
myPickupObject.set_pickupType(PickupType.QUANTITY);
Here is a way to allow container entity to wait in a Pickup for some time t and then leave with whatever entities have been picked up. The example model looks like this:
There are two key components:
ReleaseOnTimeout Dynamic event which has a parameter called '_agent' of type Agent and following code:
for (Object o : pickup.getEmbeddedObjects()) {
// find the Delay object inside Pickup
if (Delay.class == o.getClass().getSuperclass()) {
// remove the container from the Delay
Agent a = ((Delay)o).remove(_agent);
if (a != null) {
// send the removed container into Enter
enter.take(a);
}
}
}
In pickup on enter action contains following code: `create_ReleaseOnTimeout(10, container);
How this works:
pickup is configured to have Exact quantity (wait for) behaviour
a container object enters Pickup block pickup
on entry a dynamic event ReleaseOnTimeout is scheduled in 10 units to check up on container
if sufficient number of entities were available then container picks them up and leaves
alternatively to (4), if by the time 10 units elapsed the container is still stuck in pickup then it will be removed and put into enter