Криптол: арифметика с разной шириной - PullRequest
0 голосов
/ 22 октября 2018

Как выполнить арифметику со значениями разной ширины?В verilog нет проблем с кешированием 2 битов с 8 битами, но криптол жалуется:

cryptol> let test(x: [2],y: [8]) = x ^ y

[error] at <interactive>:1:31--1:32:
  Type mismatch:
    Expected type: 2
    Inferred type: 8

Моя первоначальная проблема: я хотел бы повернуть байты в 64-битное значение с количеством байтов для смещения в зависимостина двухбитном входе.Я изо всех сил пытаюсь заставить это работать:

cryptol> let shift (v, s:[2]) = v >>> (s*16+8)
[error] at <interactive>:1:5--1:38:
Unsolved constraint:
2 >= 5
  arising from
  use of literal or demoted expression
  at <interactive>:1:33--1:35

В интерпретаторе я могу удалить спецификацию типа s, и тогда она работает, однако мне нужно, чтобы это работало из файла, а s является действительно 2-битным значением.

1 Ответ

0 голосов
/ 27 октября 2018

Тип ^:

Cryptol> :t (^)
(^) : {a} (Logic a) => a -> a -> a

Обратите внимание, что оба аргумента должны быть абсолютно одинаковыми.Вы получаете ошибку типа, потому что [2] отличается от [8];как они различаются по размеру.В отличие от Verilog, Cryptol не будет «накладывать» вещи неявно, и я думаю, что Cryptol определенно поступает правильно.Программисты Verilog могут присоединиться к бесчисленным ошибкам, которые у них возникли из-за неявного приведения.

Все такие приведения в Cryptol должны быть явными.

Типичным способом решения этой ситуации в Cryptol является использованиеполиморфная константа zero:

Cryptol> :t zero
zero : {a} (Zero a) => a

Значение zero населяет все типы (вы можете пока игнорировать ограничение Zero), и, как вы можете себе представить, это "правильное" значение заполнения вэтот случай.Итак, вы бы определили свою функцию следующим образом:

Cryptol> let test(x:[2], y:[8]) = (zero#x)^y
Cryptol> :t test
test : ([2], [8]) -> [8]

И использовали бы ее так:

Cryptol> test (1, 5)
0x04

И если бы вы по какой-то причине захотели заполнить справа, вы быdo:

Cryptol> let test(x:[2], y:[8]) = (x#zero)^y
Cryptol> test(1,5)
0x45

Таким образом, все явно, и вам не нужно знать все магические правила о том, как вещи дополняются, чтобы получить нужный размер.

Если вы хотитеполучить реальную фантазию, тогда вы можете сделать:

Cryptol> let test(x, y) = (zero#x)^(zero#y)
Cryptol> :t test
test : {n, m, i, j, a} (Logic a, Zero a, m + i == n + j, fin n,
                        fin m) =>
         ([i]a, [j]a) -> [m + i]a

Теперь этот тип выглядит немного страшно;но, по сути, он говорит вам, что вы можете дать ему аргументы любого размера, и он будет действителен для любого другого размера, при условии, что новый размер больше, чем максимум двух, которые вы дали.Конечно, этот предполагаемый размер гораздо более полиморфен, чем вы, возможно, заботитесь;так что вы можете дать ему что-то более читабельное:

test : {m, n} (fin m, fin n) => [m] -> [n] -> [max m n]
test x y = (zero#x) ^ (zero#y)

Я считаю, что это отлично отражает ваши намерения.Обратите внимание, как cryptol обеспечит конечность ваших входных данных, и вы получите максимум из двух указанных размеров.

Возвращаясь к вашему примеру, Cryptol говорит вам, что для умножения на 16 вам нужно как минимум 5 бит,и, таким образом, 2>=5 не является удовлетворительным.Это немного загадочно, но возникает из-за использования литералов, которые полиморфно типизированы.Вы можете использовать трюк zero для решения проблемы таким же образом, как и раньше:

Cryptol> let shift (v, s:[2]) = v >>> ((zero#s)*16+8)
[warning] at <interactive>:1:32--1:38:
  Defaulting type argument 'front' of '(#)' to 3

Но обратите внимание, как cryptol предупреждает вас о типе zero, который там используется, так как тип>>> является достаточно полиморфным, чтобы допускать сдвиги / повороты разных размеров:

Cryptol> :t (>>>)
(>>>) : {n, ix, a} (fin n, fin ix) => [n]a -> [ix] -> [n]a

В этих случаях Cryptol выберет наименьший возможный размер по умолчанию, глядя на выражения.К сожалению, здесь это не так.Выбрав размер 3 для zero, вы получите сдвиг 5 бит, но ваше выражение может вывести максимальное значение 3*16+8=56, для представления которого требуется не менее 6 бит.Обратите внимание, что Cryptol использует только минимальный размер, необходимый для умножения, и не заботится о переполнениях!Вот почему важно обращать внимание на такие предупреждения.

Чтобы было ясно: Cryptol сделал правильную вещь в соответствии с правилами языка о том, как работает вывод типов, но в итоге он выбрал размерэто слишком мало для того, что вы хотели сделать.

Итак, вы должны написать свой shift следующим образом:

Cryptol> let shift (v, s:[2]) = v >>> (((zero:[4])#s)*16+8)
Cryptol> :t shift
shift : {n, a} (fin n) => ([n]a, [2]) -> [n]a

Здесь важно убедиться, что выражение s*16+8 будет соответствовать конечному результату, и, поскольку s имеет ширину всего 2 бита, наибольшее значение будет 56, как обсуждалось выше, для представления которого требуется по меньшей мере 6 бит.Вот почему я выбрал [4] в качестве размера zero.

Мораль этой истории заключается в том, что вы всегда должны четко указывать размеры ваших битвекторов, а Cryptol предоставит вам правильную структуручтобы выразить свои ограничения полиморфным способом, позволяя повторное использование кода без двусмысленности, избегая многих ловушек Verilog и других подобных языков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...