ppisa commented on code in PR #12081:
URL: https://github.com/apache/nuttx/pull/12081#discussion_r1564981359


##########
arch/arm/src/samv7/sam_qencoder.c:
##########
@@ -270,11 +316,139 @@ static int sam_reset(struct qe_lowerhalf_s *lower)
 static int sam_ioctl(struct qe_lowerhalf_s *lower, int cmd,
                      unsigned long arg)
 {
-  /* No ioctl commands supported */
-
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+  switch (cmd)
+    {
+      case QEIOC_GETINDEX:
+        {
+          /* Call the qeindex function */
+
+          sam_qeindex(lower, (struct qe_index_s *) arg);
+          return OK;
+        }
+
+      default:
+        {
+          return -ENOTTY;
+        }
+    }
+#else
   return -ENOTTY;
+#endif
 }
 
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+/****************************************************************************
+ * Name: sam_qe_pos_16to32b
+ *
+ * Description:
+ *   An inline function performing the extension of current position.
+ *   Last reading is saved to priv->last_pos.
+ *
+ ****************************************************************************/
+
+static inline int32_t sam_qe_pos_16to32b(struct qe_lowerhalf_s *lower,
+                                         uint32_t current_pos)
+{
+  struct sam_lowerhalf_s *priv = (struct sam_lowerhalf_s *) lower;
+
+  uint32_t new_pos = *(volatile uint32_t *) &priv->last_pos;
+  new_pos += (int16_t) (current_pos - new_pos);
+  *(volatile uint32_t *) &priv->last_pos = new_pos;
+
+  return (int32_t) new_pos;
+}
+#endif
+
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+/****************************************************************************
+ * Name: sam_qe_indx_pos_16to32b
+ *
+ * Description:
+ *   An inline function performing the extension of the last index position.
+ *   Last reading is saved to priv->last_index.
+ *
+ ****************************************************************************/
+
+static inline int32_t sam_qe_indx_pos_16to32b(struct qe_lowerhalf_s *lower,
+                                              uint32_t current_indx_pos)
+{
+  struct sam_lowerhalf_s *priv = (struct sam_lowerhalf_s *) lower;
+
+  uint32_t new_index = *(volatile uint32_t *) &priv->last_pos;

Review Comment:
   The cast can be even to uint16_t but it has to be uintX_t, because modulo 
arithmetic is only defined for unsigned types in C language. At the moment, 
where two signed types are subtracted then if there has been overflow or result 
does not fit into target type then the behavior is undefined. On the other hand 
subtraction of unsigned types has precisely defined behavior. In the fact you 
get internally signed type which has sign as an additional bit. When size is 
limited then it again works with larger unsigned type to have defined behavior 
when overflow at or over type module (2 power to n-bits) appears or if there is 
underflow under zero.
   
   If the type is not unsigned then following operation after overflow do not 
have defined behavior.  So that is why the sequence is  
   
   ```
     uint32_t new_index = *(volatile uint32_t *) &priv->last_pos;
     new_index += (int16_t) (current_indx_pos - new_index);
     *(volatile uint32_t *) &priv->last_index = new_index;
   
     return (int32_t) new_index;
   ```
   
   When we consider that even for some long distance car track or some pump 
control for speed etc.  even extended 32-bit value can overflow then even 
operations in modulo should be used for controllers in user-space. So it would 
be better to return both, IRC value and  index as 32-bit unsigned. But because 
the NuttX standard is given as signed and for some mechanic with motion  within 
limited range, it is more natural to use +/- range around zero, we keep signed 
output. That is why last_pos is kept as uint32_t even that values visible to 
user-space are signed. Copy with volatile then ensures operation on the copy 
taken by single read which ensures correct operation even when code is accessed 
from multiple threads even on multiple CPUs in parallel.
   
   As for the choice between unit16_t and uint32_t for values which are limited 
to int16_t after subtraction, on ARM there are no native operations for 16 or 
8-bit wide types, so range limitations has to be introduced by additional 
machine operations during each arithmetic operation. Return values and 
arguments to the functions are passed in full 32-bit register in the most 
cases. So again limitation to 16-bits results in abundant instructions. They 
can be eliminated when static inline is used because GCC can find that these 
problematic bits do not contribute to the results when intermediate value is 
limited to int16_t. Even if we consider x86 in 32-bit and 64-bit modes, then 
8-bit and 32-bit operations are natural, 16-bit ones require additional prefix 
and cause problematic third (or with CC fourth) input dependency to 
instructions. x86 64-operations require REX prefix but there is optimization 
for them and 32-bit ones zeros top part so no additional dependency is 
introduced. So in 
 general 16-bit integers are generally problematic for most of today processors 
and should be used only to store results into memory if data are large and 
limited range is enough. FP16 and Co. and 16-bit vector elements are different 
story on GPUs and multimedia extensions...



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to