Hello, this is Dr. Miller, and this
is Episode twelve point one of
Assembly.
Today, we're going to talk about
vector registers and then neon,
so vector registers,
so the vector registers and the
vector floating point co processor
is allows us to do floating
point operations.
It in traditionally was
a separate code processor.
So it performed these floating point
operations that the regular
processor didn't have the ability to
do.
And then going forward, if this has
been deprecated in favor of the neon
architecture, which uses larger
registers.
And then there's a difference
between scalar and a vector.
So when you're talking about the
VPP, we have different
registers.
For example, we have the floating
point status and control
register, which has information
about the different flags.
We also have additional registers.
So we have ones that are with S and
then some register numbers.
So those are thirty two bits.
The D ones start with sixty, have
sixty four bits and the Cubans
have one hundred twenty eight bits.
And then we have a set of what are
called scalar registers that
can do scalar operations.
So that's s zero through a seven
and then D zero through D three.
And these end up representing the
same sets of bits and we'll see that
in the diagram here and a little
bit.
But we also have vector registers.
So those are eight three S thirty
one D four through D
thirty one and then Q zero through Q
fifteen.
And then when you're doing vector
operations using
VPP, you have this
wraparound if the stride
in the bit links are set.
And so that allows it to do
wrapping operations.
And this is in older versions of ARM
and it's deprecated.
So for example, you'll
see some of these, but they won't
work on devices like a Raspberry Pi,
which I've been using in this class.
So here's a typical layout of the
scalar and vector registers, and
this will be the same for neon as we
go forward.
So we have registers as zero and
S1 and those composed together
would be registered D zero.
So it's similar to X eighty six.
So we have smaller registers
which then look like they're
composes a larger register
and then for example.
Q Zero is all of
these registers put together.
So it's all of a e0 and D one,
which in turn is is S
zero S1 as two and S
three.
Same thing for each one of these key
registers.
And then you have the notion that we
have these different scalar
operations that we can do and
we can take a scalar and multiply it
by a vector.
And so these in that
parlance are the vector registers
and can be used for vector
operations.
When we get to Nehan, we will
not have as many of these
restrictions and so
you'll just be able to use any of
the registers for that.
And so that was a change in the
architecture.
But here, this just shows zero three
zero through Q7 or
Reges s S1 through S
thirty one. So we have thirty two
registers, we have 16
D registers and then we have
eight key registers in this diagram.
But we have the ability
to do debugging and
so you can do info float
and that'll show the status
register status and control
register.
It also will just show registered as
S03 as 31.
And so if you're looking at it and
you want to see the floating point
or an integer in S zero to S
thirty one, this is a good
way to do it.
You can also do info vector, which
will print all of the registers,
which can be a little bit
overloading, but
then also in GDB you can just print
a single register so you can say
print dollar sign zero
zero accuser or whatever number that
you want to print.
And so that's probably the easiest
way to look at those registers
and make sure that they have the
values that they think that they
should have.
So neon so neon
is is arms
advanced amd or
single instruction, multiple
data for ARM.
And so they took the VPP and
they basically scaled it up and
allowed it to do more things.
So in floating point,
the VPP, we could just do floating
point operations.
But with neon we can also
do operations on integers.
It also has some ability to do some
parallel processing.
So when we looked at the VPP,
it only had the ability to basically
go off and run in
separate mode and then come back
with the result.
Whereas Nean, we can just put the
all the instructions together
and then Neon's started with the
Cortex eight and some of the
newer ones.
But you can add it to sort
of any of the newer types of
architectures that that arm is
producing.
And then NEON will allow us to do
either sixty four, one hundred
twenty eight bit ACMD
operations and you can use
those for things like GPS or M2
MP three decoding.
And so when we talk about Samed
we will look at those
types of operations we can do and
sort of get our appreciation for
the power that it has.
So here are some example neon
instructions so we can
do so. The V stands for
Vector and that's what
almost all the neon instructions
will have in front of them.
So we have things like absolute
value. We can add some registers,
we can do a comparison,
we can do a duplication.
So you can copy a bunch of data
to all lanes of the vector.
We can load a value.
We can figure out what the men and
the masses of different
numbers we can do, movement and
multiplication.
And so these are some examples.
You can go to the developer pages
and see the full specification
of all of these and more that
I didn't include.
So the vector
load, so here's an example of an
instruction, so we got a vector load
register. So Velda, are
we'll talk about conditional
programing later and then
we can basically do a data type.
So the data type
is
one of these.
So we got AI and then a number or
s.
So I stands for Integer, stands
for assigned U, stands for
unsigned F stands
for Floating Point, and
then these can be either eight,
sixteen, thirty two or sixty four.
So I can load an integer that's
eight bytes or sixteen or thirty
two or sixty four and
it could be signed, it could be
unsigned. And then we got the two
floating point so we got either
thirty two bits floating point or
sixty four before floating point.
And then here we can just load a
direct constant in
for that and then we can
either loaded into a deregister
or an S register.
So remember D is going to be
sixty four bits.
S is going to be thirty two bits.
We can also do a vector move and
so we can do
either move or move and invert.
So this is a movie and this is
moving invert.
And then you again, we can have
conditional execution.
We can add any of these data types.
And so typically it's going to load
them in.
I'm in a similar way so you can load
integers or floating point
and then you can either loaded into
a quad register or a
which is one hundred twenty eight or
D register, which is 64.
So here are some examples so we can
do a regular move in to register our
nine of the value
zero five and
then we can copy from our nine
into S3.
So we have to use the V move
operation in order to copy data into
that register.
You could load an immediate into
a queue register, right.
So that's going to put the number
zero in that queue register.
I remember again that for these
right, we can only do so many bits
because each of these instructions
takes up some number
of bits and we can only put four.
All the instructions in ALM are
four bytes. And so
there are some limitations to what
you can do with some of these V move
operations.
But you can do things like using the
vector load. So, for example, I
can load a
variable into R zero
and then I can use this Eldard
D and so that is double
wide.
And so what it's going to do is when
I say that if I give it the register
R2, it's going to load the
data from zero into our two
and our three because I added this D
so I basically put two
registers where sixty four bits,
thirty two in this register, thirty
two in this register.
And then if I do a V move
I can copy basically
R2 and our three into S4,
so it'll copy both of them because
it's going to read
all of the bits into there.
So we have additional examples.
So the vector move
long.
So one of the things when,
when they're doing this is they have
the ability to basically do sine
extension.
So either you can do an extension.
So if it's a negative number, you
keep that sine bit and you fill it
up or a zero extension, which
would allow you to
have integers and they just get into
a bigger register so they'll get
twice their length and it'll
get put into a larger register.
We have moving narrow, so we're
going the opposite way.
So you're copying the
smallest half of the register
and then basically some bits are
going to get thrown away.
So you'd have to understand that
this end is going to stand for the
narrow part.
And then we also have
additional move in narrow with
with saturation or
sort of the very complicated new
keyword.
And it's got of saturating narrow
into an unsigned result.
So you won't need these
most of the time, but the ARM
documentation will tell you exactly
what they do. And then you can
always create examples and see
if you understand how they work.
So here is the from the ARM
documentation.
So for example we got mov l
so we're going to
move it, move and extend
from a 30
or 64 bit register and to
a one hundred and twenty eight bit
register.
And again we got the other ones
which I won't go over necessarily
here but.
Right. If it's, if it's got a queue
behind it, that means that we're
going to go to a keyword.
We get we additionally have
conditions and then
we got the destination register and
in some of these.
Right. So Cuddy's and and
D.M.
or here these have a different
destination register for the for
the narrowing operation.
And then here we can give you the
type. So we got signed unsigned
integers
for all of these
different operations and there's
different combinations of them.
So let's look at an example here.
So, for example, we
can load e0 with this
number. So I got one, two, three,
four, five, six, seven, eight.
I remember these are each going to
be into bite pairs.
So seven, eight is one bite
and we can see that down here.
I also move A1, B1 71
into S1 and then
we do a move and extend
into
Q nine from DL.
So remember s zero and one
are the parts of
DRL. And so when I move that
as an as an eight byte,
eight bit integer.
Right. So unsigned, eight bits,
we can see that when it goes here we
add some extra zeros in here.
Right.
So those extra zeros are
making it so that we go from
an eight bit and eight, an
eight bit integer to a sixteen bit
integer. So when I look at the
sixteen bit integer there's actually
byte here of zero.
Right. So zero zero zero
zero. So you can see it intersperse
zeros in between all of those.
And then here we have another
example. So into Q10,
we move 16
bit integers into into
it. So we can see here that
the seven, eight and five six remain
the same and then we add two pairs
of zeros and then three, four and
one to remain the same.
And we add two zeros so
that when you go from we have a
the 16 bit here.
Right. Which is what we started
with, we have 16 bits here.
And then we add another 16 bits of
zeros on the top for each
one.
And so you're sixteen bit
integer here, became
a thirty two bit register.
So the value got bigger
so that we can use that.
And so in these we have basically
four integers that just all of a
sudden went from
sixteen bits to thirty two bits
each.
We can also do operations like the
vector multiply so we can say
Moule, which has a Q register
or VML in there.
And so.
We can have any of these data
types, so we got some integers
and we got some floating points, and
so then you can do multiplication on
these.
So basically the elements of the
vector are multiplied together.
So we have an example here.
So, for example, if I move two
and four into a zero
and this one and then
three and seven into two and
three, when I try and multiply
D zero and D one together, the
result is going to get stored in D
one.
So we'll take
the first element and multiply it by
D one. The first element because
we're doing 32 bit multiplication.
Right. And these are 64 bit
registers.
So I have two times three and that
gives me six and then I have
seven times four.
And so that gives me twenty
eight. Which in Hexis one C.
So we can kind of get an idea
of the multiple processing,
because I was able to just multiply
two numbers, two
sets of numbers together and get the
result in one instruction
and we'll see more examples of that
in the future.
So the benefit of the vector
operations is it allows us to
do wider math operations.
So we have the normal 30 to bit on
the registers, but it can overflow
into those 64 bits
and then we can do 64 bit operations
that go into one hundred and twenty
eight bits.
And as we saw in that last example,
we can do multiple operations in one
instruction which will help with the
efficiency of the processor.
So today, we talked about some of
the vector registers, the history
and kind of how they're laid out,
and then we talked about the neon
architecture and some examples of
that.
So in the future, we're going to
talk about some more examples
of vector instructions, so we'll
give an example of how to do
some integer multiplication
while also then look at floating
point operations.
So we didn't go into many floating
point. We'll do some examples of
that.
And then we'll also look at the Neon
ACMD operations.
So here's our quiz.
How big are each of the vector
registers?
So the stratospheres, 32 bit,
the deregister is a 64 bit and the
queue registers one hundred twenty
eight bits,
if you have questions, you can
e-mail me Miller you and
to you and you can find me on
Twitter at now 30.