[blog] Group working: constructive and destructive behaviors

4x100m Medley Relay: even though they perform alone in the pool, the overall result is what matters. In a pretty well balanced team, like this, each swimmer can go for his best style. If the team was heavily unbalanced, at the limit, for instance, Adrian happens to be competitive only on butterfly – Phelps could give up his best stroke (butterfly) for the overall goal – since Phelps’ crawl could deliver at least enough contribution for a medal.

During my career I have had the opportunity to work with different people, from different cultures (5 cities, 3 countries) and I think I can talk about my very own impressions of what is a functional working group.

I believe in a very straight way to work in group: comradery. If you can provide what others need – do it. I am not saying you need to sacrifice yourself, but the overall result should be more important than your very own result, as in any system. Frustrated colleagues will, even if they try to avoid, create a frustrating environment, and that is language – not a mystical energy as one could suggest. The size of your RAM memory does not mean too much if the throughput is damaged at a certain frequency.

But it all comes down to language: human language. It is such a complex mechanism: a biopsychosocial product from the structure of your bones, passing through the consequences of the most joyful or painful events you went through and the positions you someway have occupied on the groups you once belonged or still belong to (family, school, sports, etc.). And of course, everyone has different bones and a life story, therefore, different ways to communicate. By communication, I mean transferring, receiving and processing information. This is even more clear when working with people from different cultures.

Being helpful makes who helps feel fine – mainly if recognized. Being helped makes people feel included, and not apart or ignored. An ideal system has fully functional decoupled components that communicate well. No noise, no jitters, no heavy dependence between them, but if one component malfunctions/fails, the overall system will eventually malfunctions/fails, otherwise it is not a system at all.

The tricky thing in all of this is the balance. There will be times when a person will need to provide more than receive, and another times, this very same person will need to receive more than provide. This sending+receiving addend, should integrate in time to an ideal value, as a contribution to the ideal system value, i.e, a goal. It seems the value for the overall system is more meaningful and easier to assess than the value for each component, but the stability of the system depends on the harmony between the components1. And there are deadlocks: helpless people cannot help. And the capacity of anyone to help is also limited. If someone is not receiving, it is very probable he will not be able to provide. What comes first? That is the tricky balance.

Anyway, as individuals the best we can do is to be self-aware. Some psychologists say behaviors can generally be classified as constructive or destructive. Below some attitudes are listed2. It is worth to think about how you are doing.

Constructive Behaviors:
Cooperating: Is interested in the views and perspectives of the other group members and is willing to adapt for the good of the group.

Clarifying: Makes issues clear for the group by listening, summarizing and focusing discussions.

Inspiring: Enlivens the group, encourages participation and progress.

Harmonizing: Encourages group cohesion and collaboration. For example, uses humor as a relief after a particularly difficult discussion.

Risk Taking: Is willing to risk possible personal loss or embarrassment for the group or project success.

Process Checking: Questions the group on process issues such as agenda, time frames, discussion topics, decision methods, use of information, etc.


Destructive Behaviors:
Dominating: Takes much of meeting time expressing self views and opinions. Tries to take control by use of power, time, etc.

Rushing: Encourages the group to move on before task is complete. Gets “tired” of listening to others and working as a group.

Withdrawing: Removes self from discussions or decision-making. Refuses to participate.

Discounting: Disregards or minimizes group or individual ideas or suggestions. Severe discounting behavior includes insults, which are often in the form of jokes.

Digressing: Rambles, tells stories, and takes group away from primary purpose.

Blocking: Impedes group progress by obstructing all ideas and suggestions. “That will never work because…”

1 We are not that predictable, although being predictable is an essential good social adaptive response.

2 https://cns.utexas.edu/images/CNS/TIDES/teaching-portal/Constructive_and_Destructive_Group_Behaviors.pdf

[blog] Synchronous versus asynchronous reset on ASIC Design

Reset is about getting a system back to a known initial state. Temporary data is flushed. When I say data I mean 0s and 1s. Everything will be put to a known state so the circuit starts up. Sounds important.

When we go for synchronous or asynchronous reset, active low, our HDL code will look like one of these:

module synch_reset_pipeline(in1, in2, ..., out1, out2,...);
// #1 module with synchronous reset
always @(posedge clk) 
begin
  if (!rst) begin
   // reset logic: put this module to known state 
  end
  else begin
  // functional logic: processes data
  end
end
endmodule
module asynch_reset_pipeline(in1, in2, ..., out1, out2,...);
// #2 module with asynchronous reset
always @(posedge clk or negedge rst) 
begin
  if (!rst) begin
   // reset logic 
  end
  else begin
  // functional logic
  end
end
endmodule

To simplify, firstly, think about a single sequential element (a flip-flop). Then, imagine it is starting up, catching the very first data signal arriving on the input and propagating it to other pipeline stages. How would be a desirable reset signal? I would say:

  • it resets the circuit to a known state
  • not prone to any metastability
  • the duty cycle is just long enough to meet the above criteria

Given the above bullets what is the go for approach? Whatever fits the system better, on area, timing and power. On module #1, we are telling the tool that the reset signal is under the domain of the clock, and the reset logic will take place when reset level is low. So when the clock positive edge triggers and reset is asserted, the flops that are connected to that logic will reset. Therefore, the reset signal is pretty much another data signal (like: assign current_input = (!rst_sel) ? reset_value : next_input), selecting between the next value to be sampled or the reset data value, when a reset happens. And we should make sure it will be filtered from glitches and never prone to any meta-stability – but this is valid to any other external input too. It cannot take the fastest way through the datapath when asserted, otherwise it might reset the logic violating setup requirements, and therefore put it to a unknown state, that will be sampled and propagated. The deassertion by its turn need to respect the hold time of the signals that were propagated through the pipeline. As data and reset are on the same clock domain, the work relies pretty much on the clock tree synthesis. It costs more circuitry in general. (Although flops are smaller, they say)

On #2, the asynchronous reset is a high priority interruption, per say. The or inside the sensitivity list changes everything. Whenever reset is low (asserted) it comes to bring everything down and up again. If the pulse has a very low duty cycle, maybe the flop will not be stable yet, and will put the the circuit to an unknown state when reset is released; maybe a very bad combination (boom). That is, we need to model this asynchronous path to respect a time after asserting and before deasserting the signal, so we make sure every pipeline stage is at a known state. To prevent timing violation when asserting, we need to make sure the reset will take place respecting the (worst) removal time, so the initial data to be sampled is valid. For deasserting, we need to make sure the clock samples a stable value, by respecting the (worst) recovery time. Besides, you need to tell the tool that the signal that multiplex the input to your flops between operation data and reset data is a false path – that is the clock must ignore the impact of this signal on the data path, and not make any efforts to support setup and hold timings of the sequential elements. You will take care by yourself by controlling the reset behavior. A technique could be to model the buffers safely enough to charge and discharge on a determined relative time, so the reset removal and recovery time are always safe. That is you fix a positive delay for reset assertion and a negative delay for deassertion, regarding the active clock edge. You will need to synchronize the input reset signal to a common in both aproaches to mitigate metastability. Asynchronous reset costs less circuitry, but is also more complex: a very critical asynchronous control signal is on the game.

[blog] Tip: C variables and pointers: declarations and definitions

It might sound naive, but it is a very common interview question, and I actually think the notation can be deceiving. And furthermore, it is essential. A few months off and you will be asking yourself “how is that again?”. Unless you get this rule: start reading from right to left.

Question: Please read the following declarations and explain their properties regarding read and write.

#1 int v;

Reads as v is a variable of integer type

int v; //declaration
int same_scope_var = 0x10; //declaration and definition
v = 0; //definition
v = same_scope_var; //redefinition v=0x10

After the scope (declaration) and initial value of v is defined, you can read and write it in run-time.

#2 int const v;

Reads as v is a constant integer, i.e., read-only

After the scope and initial value of v is defined, you can only read it in run-time.

#3 int *v;

Reads as v is variable pointer to an integer.

int *v; //declaration
int same_scope_var = 0xFFFFFFFF; //declaration and defitinion
int same_scope_var2 = 0xF0FFFFFF; //declaration and defitinion
v = &same_scope_var ; //definition
v = &same_scope_var2 ; //definition

After the scope and initial value (that is a memory address that holds an integer) of v is defined, you can change it to whatever other integer address you want in run-time.

#4 int const *v;

Reads as v is variable pointer to a constant integer; that is, the address v holds can change, although it is expected to point to a constant value.

int const same_scope_k = 0xFFFFFFFF; //declaration and defitinion
int const same_scope_k2 = 0xF0FFFFFF;
int same_scope_var = 0xF0FBFFFF;
int const *v; //declaration
v = &same_scope_k; //definition
v = &same_scope_k2; //redefinition
*v = same_scope_k; //error wont compile
v = &same_scope_var; //probably compile with a warning

After the scope and initial value of v is defined, you can write and read it whenever you want in run-time, but you cannot change the value it points to by dereferencing.

#5 int * const v;

Reads as v is a constant pointer to a variable integer, i.e., v is ready-only, but the value that it points to (dereference) can change.

int same_scope_variable  = 0xFFFFFFFF; //declaration and definition
int same_scope_variable2  = 0xF0FFFFFF; 
int * const v; //declaration
v = &same_scope_variable  ; //definition
*v = same_scope_variable2; // redefinition for same_scope_variable = same_scope_variable2

After the scope and initial value of v is defined, you can only read the address it points to in run-time, but you can change the value the pointed address holds, by dereferencing.

#6 int const * const v;

Reads as v is a constant pointer to a constant integer. That is, the value v holds is a read-only address that points to a read-only integer. Its more usual to see that as a function argument when you want to be sure the method cannot change neither the address v holds nor the value it dereferences.

int const same_scope_k  = 0xFFFFFFFF; 
int const same_scope_k2  = 0xF0FFFFFF; 
int const * const v; //declaration
v = &same_scope_k; //defined, read-only
v = &same_scope_k2; //error wont compile
*v = same_scope_k2; //error wont compile

The code below should compile with no warnings. It is on a PC, therefore memory addresses have 8 bytes

#include <stdio.h>
int main()
{
    int my_variable = 0x30; 
    int const my_constant = 0x60;
    //#1 defined and declared v 
    int v = 0x05; 
    
    //#2 defined and declared read-only const
    int const v_k = 0x10; 
    int const v_k2 = 0x11;
    
    //#3 defined and declared v_ptr as the address of v
    int *v_ptr = &v; 
    
    //#4 declared and defined this as a pointer to a constant 
    int const *v_ptr_to_k = &v_k; 
    
    //#5 declared and defined this as a constant pointer to an variable integer
    int * const v_k_ptr = &my_variable; 
    
    //#6
    //declared and defined this as a constant pointer to a constant integer
    int const * const v_k_ptr_k = &my_constant;  
    // operations 
    // change v
    printf("v has the value %x, and v_ptr is %p\n\r", *v_ptr, v_ptr);
    v = 0x80;
    printf("v has now the value %x, and v_ptr is %p\n\r", *v_ptr, v_ptr);
    
    // changing v_ptr
    v_ptr = &my_variable;
    printf("v_ptr dereferences to value %x, and v_ptr is %p\n\r", *v_ptr, v_ptr);
    //change pointer to a constant
    printf("v_ptr_to_k dereferences to value %x, and v_ptr_to_k is %p\n\r", *v_ptr_to_k, (unsigned long int *)v_ptr_to_k);
    v_ptr_to_k = &v_k2;
    printf("v_ptr_to_k dereferences to value %x, and v_ptr_to_k is %p\n\r", *v_ptr_to_k, (unsigned long int *)v_ptr_to_k);
    //*v_ptr_to_k = 0x08; //<== not allowed!
    
    //operating with a constant pointer
    printf("v_k_ptr dereferences to the value %x, and v_k_ptr is %p\n\r", *v_k_ptr, (unsigned long int *)v_k_ptr);
    v = 0x35;
    *v_k_ptr = v;
    printf("v_k_ptr dereferences to the value %x, and v_k_ptr is %p\n\r", *v_k_ptr, (unsigned long int *)v_k_ptr);
    
    //operating with a constant pointer to a constant integer
    printf("v_k_ptr_k dereferences to the value %x, and v_k_ptr_k is %p\n\r", *v_k_ptr_k, (unsigned long int *)v_k_ptr_k);
    //*v_k_ptr_k = 0x92; <== not allowed!
    //v_k_ptr_k = &my_constant; <== not allowed!
    return 0;
}

Output:

(Erratum: in the printf above v_ptr_to_k was mentioned as v_ptr_k but the variable declared and defined on the code is v_ptr_to_k.)