assembly 在PicoBlaze装配中使摄氏度到华氏度转换器能够在更大范围的可能输入下工作

oiopk7p5  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(79)

我制作了a short PicoBlaze assembly program,它将使用8个开关(输入0)输入的摄氏度转换为华氏度,并使用公式Fahrenheit=(Celsius*9/5)+32将它们输出到8个LED(输出0):

;Let's say Celsius is on input 0
;and you are supposed to output
;Fahrenheit on output 0.

address 0
input s0, 0
load s1, 0
load s2, 9
multiplication_by_9:
add s1, s0
sub s2, 1
jump nz, multiplication_by_9
load s2, 0
division_by_5:
add s2, 1
sub s1, 5
jump nc, division_by_5
add s2, 32'd
output s2, 0
jump 0

字符串
现在,如果您使用开关输入00010010(二进制为18),它会输出01000001(二进制为65),这接近右边。如果您输入00010110(二进制为22),它会输出01001000(二进制为72),这也接近右边。
但是,如果输入00100101(二进制为37),则会发生溢出,并输出00110000(二进制为48),这与正确答案相差甚远。
那么,在这种情况下,我如何才能使它输出接近正确答案的东西呢?

n3h0vuf2

n3h0vuf21#

有许多选择。
最简单的方法是将代码升级到16位算术。我们可以称之为双精度方法。在picoblaze中,这意味着累加器使用2个寄存器,并执行两个8位加法,每个加法一个,还对高8位累加器寄存器使用带进位的加法,以完成16位加法。对于除法,picoblaze也有带进位减法。2这将是精确的,相对简单的。
由于输入值仅为8位宽,因此用于通过重复加法进行乘法的代码序列需要将输入的低字节(s 0)添加到16位累加器的低字节(s1)中,然后使用 add with carry,将常数0添加到16位累加器的高字节中(可以是S3,也被预初始化为0),以捕获并累加表示乘法的16位和。
除法也是如此,因为一个操作数(5)是一个字节宽,另一个是16位累加器。
下面的代码使用了一个额外的寄存器s3作为累加器的高阶寄存器。(它的舍入行为与原来的相同。)

;Let's say Celsius is on input 0
;and you are supposed to output
;Fahrenheit on output 0.

    address 0
    input s0, 0
    ;load s0, 37'd
    load s1, 0
    load s3, 0
    load s2, 9

multiplication_by_9:
    add s1, s0
    addcy s3, 0
    sub s2, 1
    jump nz, multiplication_by_9

    load s2, 0
division_by_5:
    add s2, 1
    sub s1, 5
    subcy s3, 0
    jump nc, division_by_5

    add s2, 32'd
    output s2, 0

label: jump label

字符串
另一个选项是乘以1.8,这将避免除以5时的高循环计数(而不是先除以9再除以5),这里也使用16位运算,但不是针对低字节和高字节寄存器,而是针对更大数量级的寄存器,一个用于整数部分,一个用于小数部分-我们认为整数部分是高字节,小数部分是低字节,这称为fixed-point arithmetic,并且例如可以使用一对整数来表示分数值。
我们可以将1.8近似为:x + x(2^-1)+ x(2^-2)+ x(2^-6)+ x(2^-7),即1.796875x,或者再加上一项x(2^-9),即1.800781x。(您可以使用更长的序列进行试验以获得更精确的结果,但在简单的二进制形式中不可能实现完全精确,虽然也几乎不值得。)每一项都有一个2的某个(负)幂的因子,这意味着这是可以用右移来完成的除法。小数部分很重要,因为它们会加起来并通知整数部分。
在这台机器上,由于一次只能移位1位,因此用于此操作的指令序列会稍微加长,但我们可以重用中间结果,例如2^-1(右移1位),我们可以再多移一位得到2^2项。这意味着有两个(16位)累加器(两者均为定点对),一个用于项的和,一个用于每次移位一位以获得项并在适当时求和的当前小数部分(即,我们想要的因子之一)。
在这里,使用带进位的加法(就像在双精度加法中一样)也是必要的--尽管因为乘以1.8已经包括除以5,所以没有进一步的除法要做,所以不一定需要带进位的减法,因为我们只是求和项。(理论上,我们也可以减去项,但快速浏览一下,这并不能提高精度或所需的项数)。
下面的代码使用s2和s3作为一个定点对,其中s2是整数部分,s3是小数部分。这意味着它们应该被视为与两个字节之间的基数点连接在一起。s4和s5的使用方式相同。s2和s3对形成累加器和应答。s4和s5对S5对是通过一次移位一位而除以2的某个负幂的当前值。
右移操作(除以2)从高位开始,并进位到低位(两次移位,第二次使用进位),而加法从低位开始,并进位到高位(两次加法,第二次使用进位)。

;Let's say Celsius is on input 0
;and you are supposed to output
;Fahrenheit on output 0.

    address 0
    input s0, 0
    ;load s0, 37'd

; s2 & s3 are the answer as fixed point pair
; used as the accumulator for the sum of terms
; s2 is int part, s3 is fractional part
; initialize the accumulator with 1x
    load s2, s0 
    load s3, 0

; s4 & s5 are the fractional component, repeatedly divided by 2
    load s4, s0
    load s5, 0

; compute (2^-1)x, e.g. divide by 2 and accumulate it
    sr0 s4
    sra s5
    add s3, s5
    addcy s2, s4

; compute (2^-2), e.g. divide by 4 and accumulate it
    sr0 s4
    sra s5
    add s3, s5
    addcy s2, s4

    sr0 s4 ; shift but don't sum
    sra s5

    sr0 s4 ; shift but don't sum
    sra s5

    sr0 s4 ; shift and sum 
    sra s5
    add s3, s5
    addcy s2, s4

    sr0 s4
    sra s5
    add s3, s5
    addcy s2, s4

    sr0 s4
    sra s5

    sr0 s4
    sra s5
    add s3, s5
    addcy s2, s4

    add s2, 32'd
    output s2, 0

label: jump label


因此,将定点16位寄存器对一次移位一位,然后根据我们是否对2的特定幂感兴趣来求和,以得到1.8x。
我们还要注意,序列运行时没有循环(没有重复/迭代的加法或减法),所以性能非常好!
无论使用哪种方法,您都可以选择舍入到更接近的值,甚至输出某个小数值。
在第一种方法(双精度)中,我们可以使用除以5后的余数来告诉我们如何舍入(余数>= 3建议将整数部分向上舍入)。
在后一种方法(定点运算)中,我们可以使用小数字节来决定舍入(例如,如果s3 >= 0x 80,则将整数部分舍入1 - 0x 80,因为定点小数部分意味着0.5)。

相关问题