开云体育

ctrl + shift + ? for shortcuts
© 2025 开云体育

How to cast between integer types?


 

I have two quick questions :)
?
1. Is there a way to cast between Bit #(n) and Bit #(m) with generic n and m without requiring any provisos? (automatically truncating/extending as needed)
2. How do you cast between Int #(n), UInt #(n) and Bit #(n)? The only way I could find was unpack(pack(..))...


 

I'll add 2 cents here.? ?This is a good place for you to start to develop your own functions that make sense to you.? I could never remember which order pack and unpack went, and did find it useful occasionally to go from type to type to size, etc (with all the caveats about being careful to do so).? Some of the functions I have in my MiscUtils.bsv are below.
I have no doubt that there will be at least one "irk" from my fellow Bluespec'ers.? ? But BSV is a pretty functional language also, and feel free to create your own useful functions with the usual?caveats of keeping track of what you are actually doing with these :)

Notice that BSV rightly often forces you to be explicit.? That is generally goodness, but as a RTL person from before the days of the internet, I've always like some level of my own language building.? The point is to make it easier and more productive for you to code and code correctly.? If you have to have some helper function, go for it :)

Steve

// these conversion types need to be used *VERY* carefully since
// they completely side step bluespec's strong type checking
// which means you have a greater chance of introducing typing
// errors where you didn't expect.? Still testbenching tends to
// do lots of this (i.e. take addresses, turn it into data, munge it, etc)..
// so these are provided to ease that use model

// convert from type => Bits
// i.e.? Int#(10) x;
// ? ? ? Bit#(10) y = toBits( x );
function Bit#(n) toBits( tp data ) provisos(Bits#(tp,sizetp),Add#(n,0,sizetp));
? ?return pack(data);
endfunction

// convert from Bits => type
// i.e.? Bit#(10) x;
// ? ? ? Int#(10) y = fromBits( x );
function tp fromBits( Bit#(n) data ) provisos(Bits#(tp,sizetp),Add#(n,0,sizetp));
? ?return unpack(data);
endfunction

// change from type "tn" to type "tp" where "tp" has more (or the same) number
// ? of bits and type "tn"
// i.e. Int#(10) x;
// ? ? ?UInt#(32) y = upcast( x );
function tp upcast( tn data ) provisos(Bits#(tn,sizetn),Bits#(tp,sizetp),Add#(sizetn,xsz,sizetp));
? ?return fromBits(extend(toBits(data)));
endfunction

// change from type "tn" to type "tp" where "tp" has less (or the same) number
// ? of bits and type "tn"
// i.e. Int#(32) x;
// ? ? ?UInt#(32) y = recast( x );
function td recast( tn data ) provisos(Bits#(tn,szN), Bits#(td,szD), Add#(szN,0,szD));
? ?return fromBits(toBits(data));
endfunction

// this changes any size to anysize,
// but I like knowing if size is going up or down or the same
function td cast( tn data ) provisos(Bits#(tn,szN),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Bits#(td,szD),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Add#(szN,szD,szAll));

? ?Bit#(szAll) res = extend(toBits(data));
? ?return fromBits(truncate(res));
endfunction


On Thu, Feb 27, 2025 at 9:06?AM Emily Schmidt via <aiju=[email protected]> wrote:
I have two quick questions :)
?
1. Is there a way to cast between Bit #(n) and Bit #(m) with generic n and m without requiring any provisos? (automatically truncating/extending as needed)
2. How do you cast between Int #(n), UInt #(n) and Bit #(n)? The only way I could find was unpack(pack(..))...



--
Steve?Allen /?Staff FPGA Engineer / Zoox /?


 

1. Is there a way to cast between Bit #(n) and Bit #(m) with generic n and m without requiring any provisos? (automatically truncating/extending as needed)

In the "BUtils" library, there is "zExtend".? For an input size N and an output size M, "zExtend" works by extending to size N+M and then truncating back to M.? It is written with proviso to get at the value "N+M", but that proviso is always satisfiable, so it is immediately discharged and doesn't introduce a proviso into your code. ?(The function could have been written without a proviso, using TAdd#(n,m) to get at the value; and, in fact, that's how BSC discharged the proviso -- by substituting it with TAdd.)

Another of way of writing the function would be to declare an output of size M and use a for-loop to fill in the bits (either from the input or zeros/sign bits).

2. How do you cast between Int #(n), UInt #(n) and Bit #(n)? The only way I could find was unpack(pack(..))...

Pack and unpack are the natural way to convert between Bit and Int/UInt.? To convert between Int and UInt, it would depend on what kind of conversion you want.? If you just want the same bits, then unpack(pack()) is the way to do it.? But if you want some other behavior, I think you would have to explicit write the function -- for example:

function Maybe(UInt#(n)) int_to_uint(Int#(n_plus_one) i)
provisos(Add#(n,1,n_plus_one));
? if (i >= 0)
? ? return tagged Valid unpack(truncate(pack(i)));
? else
? ? return tagged Invalid;
endfunction

J


 

2. How do you cast between Int #(n), UInt #(n) and Bit #(n)? The only way I could find was unpack(pack(..))...

I meant to add: I tend to avoid using UInt, and just use Bit for that, since they have identical behavior and it avoids needing to cast between them.? In theory, UInt might be useful if you want BSC to provide some extra type safety, by preventing you from using UInt values in functions for Bit -- but I don't recall needing it.? So I just use Bit and Int.

J


 


For another opinion, I prefer to use UInt for most types e.g. address, counts, indexes etc.? I almost never use Bit(n) except for unknown data.??
If you find the need for many casts, you might want to reconsider your data structures.

Ed.




On Thu, Feb 27, 2025 at 10:05?PM Julie Schwartz via <quark=[email protected]> wrote:
2. How do you cast between Int #(n), UInt #(n) and Bit #(n)? The only way I could find was unpack(pack(..))...

I meant to add: I tend to avoid using UInt, and just use Bit for that, since they have identical behavior and it avoids needing to cast between them.? In theory, UInt might be useful if you want BSC to provide some extra type safety, by preventing you from using UInt values in functions for Bit -- but I don't recall needing it.? So I just use Bit and Int.

J


 

Example that needs a cast: I have an index (/coordinate) that's Bit / UInt that needs to be multiplied by a signed coefficient that's an Int to do some calculation. Making the always positive index Int seems like a waste of a sign bit (and maybe would lead to needing casts somewhere else...)


 

Can you say more about what would be the waste?? If the sign bit is constant, I would expect synthesis tools to optimise it away. ?(That's assuming that BSC hasn't optimized it.? Have you created a small example and looked at the generated Verilog?)

Note that BSC offers "signedMul" and "unsignedMul" functions, as full-precision alternatives to the "*" operator.? These are documented in section 2.5.2 (Arithmetic Functions) in the BSC Libraries Guide.

And if you would prefer a multiply function that takes Int#(n) and UInt#(m) and returns Int#(TAdd#(n,m)), and that doesn't add a sign bit, you could probably write it, similar to how "signedMul" is written.? If I can give some more detail:

Multiplies in BSC are built on the following primitive:

primitive primMul :: (Add k n m) => Bit k -> Bit n -> Bit m


This becomes the Verilog "*" operator in the generated output. ? The "primMul" primitive is full-precision, because it allows multiplying any size inputs.? In contrast, BSC provides an overloaded "*" operator, but that only multiplies two values of the same size and returns a result of that size, as indicated in the type:

class (Literal a) => Arith a where

? ??(*) :: a -> a -> a?


The "*" operator for Bit, UInt, and Int is just calling the "primMul" primitive on the raw bits and then truncating the result to ignore the upper bits.? This is done in the Bit instance, that is then used by the UInt and Int instances (on the raw bits):

instance Arith (Bit n) where

? ??(*) x y? = primTrunc (primMul x y)


instance Arith (Int n) where

? ??(*) (Int x) (Int y) = Int (x * y)


On the other hand, the "signedMul" function peels off the sign bits, multiplies the absolute values at full precision, then reapplies the sign bit:

signedMul :: (Add n k m) => Int n -> Int k -> Int m

signedMul a b =

? let (s_a, v_a) = fromSigned a

? ? ? (s_b, v_b) = fromSigned b

? ? ? mulResult? = unpack(primMul (pack v_a) (pack v_b))

? in toSigned ((s_a && (not s_b)) || (s_b && (not s_a)), mulResult)


fromSigned :: Int n -> (Bool, UInt n)

fromSigned x = (x < 0, unpack (pack (abs x)))


toSigned :: (Bool, UInt n) -> Int n

toSigned (s,x) = (if s then negate else id) (unpack (pack x))


(Offhand, I'm not sure why it does it that way; if calling "primMul" on the raw bits worked for the "*" operator on Int, then just do that, but at full precision.? And since both have a sign bits, should the result be of size n+k-1?? Anyway...)

Since "signedMul" explicitly handles the sign bit, if the sign bit is constant then BSC would be able to optimize the logic on it.? However, I note that the code isn't actually peeling off the sign bit: "fromSigned" doesn't return a UInt of size n-1.? Because of that, the generated code may still include the constant bit in the multiplication -- though we'd have to run BSC on a small example and see what it generates.

In any case, this suggests how one might write a function that takes Int#(n) and UInt#(m) and returns UInt#(n+m) in a way that could give the output Verilog that you're looking for.

Julie


On Sat, Mar 1, 2025 at 3:26?AM Emily Schmidt via <aiju=[email protected]> wrote:
Example that needs a cast: I have an index (/coordinate) that's Bit / UInt that needs to be multiplied by a signed coefficient that's an Int to do some calculation. Making the always positive index Int seems like a waste of a sign bit (and maybe would lead to needing casts somewhere else...)


 

And since both have a sign bits, should the result be of size n+k-1?
?
However, I note that the code isn't actually peeling off the sign bit: "fromSigned" doesn't return a UInt of size n-1.

Never mind, I realized my mistake.? In two's complement, the lowest negative value of Int#(n) can require all n bits to represent its absolute value (as UInt#(n)).

J