Для наглядности

:
X0...X4 - выходы, управляемые буферами с тремя состояниями (выдают или '0' или 'Z');
Y0...Y1 - входы, подтянутые к питанию (тут 3, у вас будет четыре).
Делал так - имеется управляющий автомат, счетчик для задержки.
1) В исходном состоянии автомат держит счетчик в сбросе, все Х0...Х4 в '0'. На вход автомата заведено (Y0 & Y1 Y2). При появлении '0' (т.е. нажатие хотя бы одной кнопки) автомат переходит в следующее состояние.
2) Здесь автомат запускает счетчик - задержка для защиты от дребезга (обычно порядка 20-40 мс достаточно). Состояние выходов X0...X4 переводил в 'Z'. Счетчик выдает на автомат строб - автомат переходит в следующее состояние.
3) Дальше собсно сканирование - поочередно каждый из выходов X0...X4 переводится в '0' (остальные остаются в 'Z') - считываем каждую строку поочередно. Сначала получаем состояние S1, S6 и S11. Потом S2, S7, S12 и т.д. Тут поведение автомата (и соответсвенно число необходимых состояний) зависит от требований к клавиатуре - можно ли нажимать одновременно несколько кнопок или только по одной, когда завершать сканирование (до появления первого результата или надо все строки обязательно проверить) и т.д.
4) Завершение сканирования. Снова все выходы Х0...Х4 переводятся в '0' - теперь ждем когда (Y0 & Y1 Y2) станет '1' - т.е. все кнопки отжаты.
4) Снова запускаем счетчик для задержки против дребезга.
5) Возвращаемся в исходное состояние.
По поводу сымулировать - писал на МHDL элемент "кнопка" - два входа (вход-контакт и упр. вход), один выход. Если на упр. входе '1', то выход повторяет значение на входе-контакте (в т.ч. и 'Z'), иначе на выходе всегда Z. Потом сколько надо кнопок объединил в матрицу. На упр.входы каждой кнопки подавал сигнал от тестового генератора - так и моделировал (одно нажатие, нажатие нескольких кнопок).
Другой вариант - непрерывное сканирование. Защита от дребезга нужна все равно (усреднять результат по нескольким считываниям). Вопрос о формировании выходного кода - тут опять исходя из требований к клавиатуре.